Skip to content

Commit b605154

Browse files
Merge pull request #4173 from CommunityToolkit/shweaver/file-storage
Enable item renaming and add tests for ApplicationDataStorageHelper
2 parents 5171b99 + 6787487 commit b605154

File tree

4 files changed

+181
-51
lines changed

4 files changed

+181
-51
lines changed

Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public Task<T> ReadCacheFileAsync<T>(string filePath, T @default = default)
5151
/// <returns>Waiting task until completion.</returns>
5252
public Task CreateCacheFileAsync<T>(string filePath, T value)
5353
{
54-
return SaveFileAsync<T>(CacheFolder, filePath, value);
54+
return CreateFileAsync<T>(CacheFolder, filePath, value);
5555
}
5656

5757
/// <summary>
@@ -69,9 +69,20 @@ public Task CreateCacheFolderAsync(string folderPath)
6969
/// </summary>
7070
/// <param name="itemPath">The path to the item for deletion.</param>
7171
/// <returns>Waiting task until completion.</returns>
72-
public Task DeleteCacheItemAsync(string itemPath)
72+
public Task<bool> TryDeleteCacheItemAsync(string itemPath)
7373
{
74-
return DeleteItemAsync(CacheFolder, itemPath);
74+
return TryDeleteItemAsync(CacheFolder, itemPath);
75+
}
76+
77+
/// <summary>
78+
/// Rename an item in the LocalCacheFolder.
79+
/// </summary>
80+
/// <param name="itemPath">The path to the target item.</param>
81+
/// <param name="newName">The new nam for the target item.</param>
82+
/// <returns>Waiting task until completion.</returns>
83+
public Task<bool> TryRenameCacheItemAsync(string itemPath, string newName)
84+
{
85+
return TryRenameItemAsync(CacheFolder, itemPath, newName);
7586
}
7687
}
7788
}

Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs

Lines changed: 69 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System;
66
using System.Collections.Generic;
7+
using System.IO;
78
using System.Linq;
89
using System.Threading.Tasks;
910
using Microsoft.Toolkit.Helpers;
@@ -26,19 +27,19 @@ public partial class ApplicationDataStorageHelper : IFileStorageHelper, ISetting
2627
/// <param name="objectSerializer">Serializer for converting stored values. Defaults to <see cref="Toolkit.Helpers.SystemSerializer"/>.</param>
2728
public ApplicationDataStorageHelper(ApplicationData appData, Toolkit.Helpers.IObjectSerializer? objectSerializer = null)
2829
{
29-
this.AppData = appData ?? throw new ArgumentNullException(nameof(appData));
30-
this.Serializer = objectSerializer ?? new Toolkit.Helpers.SystemSerializer();
30+
AppData = appData ?? throw new ArgumentNullException(nameof(appData));
31+
Serializer = objectSerializer ?? new Toolkit.Helpers.SystemSerializer();
3132
}
3233

3334
/// <summary>
3435
/// Gets the settings container.
3536
/// </summary>
36-
public ApplicationDataContainer Settings => this.AppData.LocalSettings;
37+
public ApplicationDataContainer Settings => AppData.LocalSettings;
3738

3839
/// <summary>
3940
/// Gets the storage folder.
4041
/// </summary>
41-
public StorageFolder Folder => this.AppData.LocalFolder;
42+
public StorageFolder Folder => AppData.LocalFolder;
4243

4344
/// <summary>
4445
/// Gets the storage host.
@@ -80,7 +81,7 @@ public static async Task<ApplicationDataStorageHelper> GetForUserAsync(User user
8081
/// <returns>True if a value exists.</returns>
8182
public bool KeyExists(string key)
8283
{
83-
return this.Settings.Values.ContainsKey(key);
84+
return Settings.Values.ContainsKey(key);
8485
}
8586

8687
/// <summary>
@@ -92,9 +93,9 @@ public bool KeyExists(string key)
9293
/// <returns>The TValue object.</returns>
9394
public T? Read<T>(string key, T? @default = default)
9495
{
95-
if (this.Settings.Values.TryGetValue(key, out var valueObj) && valueObj is string valueString)
96+
if (Settings.Values.TryGetValue(key, out var valueObj) && valueObj is string valueString)
9697
{
97-
return this.Serializer.Deserialize<T>(valueString);
98+
return Serializer.Deserialize<T>(valueString);
9899
}
99100

100101
return @default;
@@ -103,9 +104,9 @@ public bool KeyExists(string key)
103104
/// <inheritdoc />
104105
public bool TryRead<T>(string key, out T? value)
105106
{
106-
if (this.Settings.Values.TryGetValue(key, out var valueObj) && valueObj is string valueString)
107+
if (Settings.Values.TryGetValue(key, out var valueObj) && valueObj is string valueString)
107108
{
108-
value = this.Serializer.Deserialize<T>(valueString);
109+
value = Serializer.Deserialize<T>(valueString);
109110
return true;
110111
}
111112

@@ -116,19 +117,19 @@ public bool TryRead<T>(string key, out T? value)
116117
/// <inheritdoc />
117118
public void Save<T>(string key, T value)
118119
{
119-
this.Settings.Values[key] = this.Serializer.Serialize(value);
120+
Settings.Values[key] = Serializer.Serialize(value);
120121
}
121122

122123
/// <inheritdoc />
123124
public bool TryDelete(string key)
124125
{
125-
return this.Settings.Values.Remove(key);
126+
return Settings.Values.Remove(key);
126127
}
127128

128129
/// <inheritdoc />
129130
public void Clear()
130131
{
131-
this.Settings.Values.Clear();
132+
Settings.Values.Clear();
132133
}
133134

134135
/// <summary>
@@ -139,7 +140,7 @@ public void Clear()
139140
/// <returns>True if a value exists.</returns>
140141
public bool KeyExists(string compositeKey, string key)
141142
{
142-
if (this.TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
143+
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
143144
{
144145
return composite.ContainsKey(key);
145146
}
@@ -157,12 +158,12 @@ public bool KeyExists(string compositeKey, string key)
157158
/// <returns>The T object.</returns>
158159
public bool TryRead<T>(string compositeKey, string key, out T? value)
159160
{
160-
if (this.TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
161+
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
161162
{
162163
string compositeValue = (string)composite[key];
163164
if (compositeValue != null)
164165
{
165-
value = this.Serializer.Deserialize<T>(compositeValue);
166+
value = Serializer.Deserialize<T>(compositeValue);
166167
return true;
167168
}
168169
}
@@ -181,11 +182,11 @@ public bool TryRead<T>(string compositeKey, string key, out T? value)
181182
/// <returns>The T object.</returns>
182183
public T? Read<T>(string compositeKey, string key, T? @default = default)
183184
{
184-
if (this.TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
185+
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
185186
{
186187
if (composite.TryGetValue(key, out object valueObj) && valueObj is string value)
187188
{
188-
return this.Serializer.Deserialize<T>(value);
189+
return Serializer.Deserialize<T>(value);
189190
}
190191
}
191192

@@ -202,17 +203,17 @@ public bool TryRead<T>(string compositeKey, string key, out T? value)
202203
/// <param name="values">Objects to save.</param>
203204
public void Save<T>(string compositeKey, IDictionary<string, T> values)
204205
{
205-
if (this.TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
206+
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
206207
{
207208
foreach (KeyValuePair<string, T> setting in values)
208209
{
209210
if (composite.ContainsKey(setting.Key))
210211
{
211-
composite[setting.Key] = this.Serializer.Serialize(setting.Value);
212+
composite[setting.Key] = Serializer.Serialize(setting.Value);
212213
}
213214
else
214215
{
215-
composite.Add(setting.Key, this.Serializer.Serialize(setting.Value));
216+
composite.Add(setting.Key, Serializer.Serialize(setting.Value));
216217
}
217218
}
218219
}
@@ -221,10 +222,10 @@ public void Save<T>(string compositeKey, IDictionary<string, T> values)
221222
composite = new ApplicationDataCompositeValue();
222223
foreach (KeyValuePair<string, T> setting in values)
223224
{
224-
composite.Add(setting.Key, this.Serializer.Serialize(setting.Value));
225+
composite.Add(setting.Key, Serializer.Serialize(setting.Value));
225226
}
226227

227-
this.Settings.Values[compositeKey] = composite;
228+
Settings.Values[compositeKey] = composite;
228229
}
229230
}
230231

@@ -236,7 +237,7 @@ public void Save<T>(string compositeKey, IDictionary<string, T> values)
236237
/// <returns>A boolean indicator of success.</returns>
237238
public bool TryDelete(string compositeKey, string key)
238239
{
239-
if (this.TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
240+
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
240241
{
241242
return composite.Remove(key);
242243
}
@@ -247,54 +248,48 @@ public bool TryDelete(string compositeKey, string key)
247248
/// <inheritdoc />
248249
public Task<T?> ReadFileAsync<T>(string filePath, T? @default = default)
249250
{
250-
return this.ReadFileAsync<T>(this.Folder, filePath, @default);
251+
return ReadFileAsync<T>(Folder, filePath, @default);
251252
}
252253

253254
/// <inheritdoc />
254255
public Task<IEnumerable<(DirectoryItemType ItemType, string Name)>> ReadFolderAsync(string folderPath)
255256
{
256-
return this.ReadFolderAsync(this.Folder, folderPath);
257+
return ReadFolderAsync(Folder, folderPath);
257258
}
258259

259260
/// <inheritdoc />
260261
public Task CreateFileAsync<T>(string filePath, T value)
261262
{
262-
return this.SaveFileAsync<T>(this.Folder, filePath, value);
263+
return CreateFileAsync<T>(Folder, filePath, value);
263264
}
264265

265266
/// <inheritdoc />
266267
public Task CreateFolderAsync(string folderPath)
267268
{
268-
return this.CreateFolderAsync(this.Folder, folderPath);
269+
return CreateFolderAsync(Folder, folderPath);
269270
}
270271

271272
/// <inheritdoc />
272-
public Task DeleteItemAsync(string itemPath)
273+
public Task<bool> TryDeleteItemAsync(string itemPath)
273274
{
274-
return this.DeleteItemAsync(this.Folder, itemPath);
275+
return TryDeleteItemAsync(Folder, itemPath);
275276
}
276277

277-
/// <summary>
278-
/// Saves an object inside a file.
279-
/// </summary>
280-
/// <typeparam name="T">Type of object saved.</typeparam>
281-
/// <param name="filePath">Path to the file that will contain the object.</param>
282-
/// <param name="value">Object to save.</param>
283-
/// <returns>Waiting task until completion.</returns>
284-
public Task<StorageFile> SaveFileAsync<T>(string filePath, T value)
278+
/// <inheritdoc />
279+
public Task<bool> TryRenameItemAsync(string itemPath, string newName)
285280
{
286-
return this.SaveFileAsync(this.Folder, filePath, value);
281+
return TryRenameItemAsync(Folder, itemPath, newName);
287282
}
288283

289284
private async Task<T?> ReadFileAsync<T>(StorageFolder folder, string filePath, T? @default = default)
290285
{
291-
string value = await StorageFileHelper.ReadTextFromFileAsync(folder, filePath);
292-
return (value != null) ? this.Serializer.Deserialize<T>(value) : @default;
286+
string value = await StorageFileHelper.ReadTextFromFileAsync(folder, NormalizePath(filePath));
287+
return (value != null) ? Serializer.Deserialize<T>(value) : @default;
293288
}
294289

295290
private async Task<IEnumerable<(DirectoryItemType, string)>> ReadFolderAsync(StorageFolder folder, string folderPath)
296291
{
297-
var targetFolder = await folder.GetFolderAsync(folderPath);
292+
var targetFolder = await folder.GetFolderAsync(NormalizePath(folderPath));
298293
var items = await targetFolder.GetItemsAsync();
299294

300295
return items.Select((item) =>
@@ -307,20 +302,47 @@ public Task<StorageFile> SaveFileAsync<T>(string filePath, T value)
307302
});
308303
}
309304

310-
private Task<StorageFile> SaveFileAsync<T>(StorageFolder folder, string filePath, T value)
305+
private async Task<StorageFile> CreateFileAsync<T>(StorageFolder folder, string filePath, T value)
311306
{
312-
return StorageFileHelper.WriteTextToFileAsync(folder, this.Serializer.Serialize(value)?.ToString(), filePath, CreationCollisionOption.ReplaceExisting);
307+
return await StorageFileHelper.WriteTextToFileAsync(folder, Serializer.Serialize(value)?.ToString(), NormalizePath(filePath), CreationCollisionOption.ReplaceExisting);
313308
}
314309

315310
private async Task CreateFolderAsync(StorageFolder folder, string folderPath)
316311
{
317-
await folder.CreateFolderAsync(folderPath, CreationCollisionOption.OpenIfExists);
312+
await folder.CreateFolderAsync(NormalizePath(folderPath), CreationCollisionOption.OpenIfExists);
313+
}
314+
315+
private async Task<bool> TryDeleteItemAsync(StorageFolder folder, string itemPath)
316+
{
317+
try
318+
{
319+
var item = await folder.GetItemAsync(NormalizePath(itemPath));
320+
await item.DeleteAsync();
321+
return true;
322+
}
323+
catch
324+
{
325+
return false;
326+
}
327+
}
328+
329+
private async Task<bool> TryRenameItemAsync(StorageFolder folder, string itemPath, string newName)
330+
{
331+
try
332+
{
333+
var item = await folder.GetItemAsync(NormalizePath(itemPath));
334+
await item.RenameAsync(newName, NameCollisionOption.FailIfExists);
335+
return true;
336+
}
337+
catch
338+
{
339+
return false;
340+
}
318341
}
319342

320-
private async Task DeleteItemAsync(StorageFolder folder, string itemPath)
343+
private string NormalizePath(string path)
321344
{
322-
var item = await folder.GetItemAsync(itemPath);
323-
await item.DeleteAsync();
345+
return Path.Combine(Path.GetDirectoryName(path), Path.GetFileName(path));
324346
}
325347
}
326348
}

Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ public interface IFileStorageHelper
5555
/// </summary>
5656
/// <param name="itemPath">The path to the item for deletion.</param>
5757
/// <returns>Waiting task until completion.</returns>
58-
Task DeleteItemAsync(string itemPath);
58+
Task<bool> TryDeleteItemAsync(string itemPath);
59+
60+
/// <summary>
61+
/// Rename an item.
62+
/// </summary>
63+
/// <param name="itemPath">The path to the target item.</param>
64+
/// <param name="newName">The new nam for the target item.</param>
65+
/// <returns>Waiting task until completion.</returns>
66+
Task<bool> TryRenameItemAsync(string itemPath, string newName);
5967
}
6068
}

0 commit comments

Comments
 (0)