Skip to content

Commit

Permalink
Merge pull request #4173 from CommunityToolkit/shweaver/file-storage
Browse files Browse the repository at this point in the history
Enable item renaming and add tests for ApplicationDataStorageHelper
  • Loading branch information
michael-hawker authored Aug 19, 2021
2 parents 5171b99 + 6787487 commit b605154
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public Task<T> ReadCacheFileAsync<T>(string filePath, T @default = default)
/// <returns>Waiting task until completion.</returns>
public Task CreateCacheFileAsync<T>(string filePath, T value)
{
return SaveFileAsync<T>(CacheFolder, filePath, value);
return CreateFileAsync<T>(CacheFolder, filePath, value);
}

/// <summary>
Expand All @@ -69,9 +69,20 @@ public Task CreateCacheFolderAsync(string folderPath)
/// </summary>
/// <param name="itemPath">The path to the item for deletion.</param>
/// <returns>Waiting task until completion.</returns>
public Task DeleteCacheItemAsync(string itemPath)
public Task<bool> TryDeleteCacheItemAsync(string itemPath)
{
return DeleteItemAsync(CacheFolder, itemPath);
return TryDeleteItemAsync(CacheFolder, itemPath);
}

/// <summary>
/// Rename an item in the LocalCacheFolder.
/// </summary>
/// <param name="itemPath">The path to the target item.</param>
/// <param name="newName">The new nam for the target item.</param>
/// <returns>Waiting task until completion.</returns>
public Task<bool> TryRenameCacheItemAsync(string itemPath, string newName)
{
return TryRenameItemAsync(CacheFolder, itemPath, newName);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Toolkit.Helpers;
Expand All @@ -26,19 +27,19 @@ public partial class ApplicationDataStorageHelper : IFileStorageHelper, ISetting
/// <param name="objectSerializer">Serializer for converting stored values. Defaults to <see cref="Toolkit.Helpers.SystemSerializer"/>.</param>
public ApplicationDataStorageHelper(ApplicationData appData, Toolkit.Helpers.IObjectSerializer? objectSerializer = null)
{
this.AppData = appData ?? throw new ArgumentNullException(nameof(appData));
this.Serializer = objectSerializer ?? new Toolkit.Helpers.SystemSerializer();
AppData = appData ?? throw new ArgumentNullException(nameof(appData));
Serializer = objectSerializer ?? new Toolkit.Helpers.SystemSerializer();
}

/// <summary>
/// Gets the settings container.
/// </summary>
public ApplicationDataContainer Settings => this.AppData.LocalSettings;
public ApplicationDataContainer Settings => AppData.LocalSettings;

/// <summary>
/// Gets the storage folder.
/// </summary>
public StorageFolder Folder => this.AppData.LocalFolder;
public StorageFolder Folder => AppData.LocalFolder;

/// <summary>
/// Gets the storage host.
Expand Down Expand Up @@ -80,7 +81,7 @@ public static async Task<ApplicationDataStorageHelper> GetForUserAsync(User user
/// <returns>True if a value exists.</returns>
public bool KeyExists(string key)
{
return this.Settings.Values.ContainsKey(key);
return Settings.Values.ContainsKey(key);
}

/// <summary>
Expand All @@ -92,9 +93,9 @@ public bool KeyExists(string key)
/// <returns>The TValue object.</returns>
public T? Read<T>(string key, T? @default = default)
{
if (this.Settings.Values.TryGetValue(key, out var valueObj) && valueObj is string valueString)
if (Settings.Values.TryGetValue(key, out var valueObj) && valueObj is string valueString)
{
return this.Serializer.Deserialize<T>(valueString);
return Serializer.Deserialize<T>(valueString);
}

return @default;
Expand All @@ -103,9 +104,9 @@ public bool KeyExists(string key)
/// <inheritdoc />
public bool TryRead<T>(string key, out T? value)
{
if (this.Settings.Values.TryGetValue(key, out var valueObj) && valueObj is string valueString)
if (Settings.Values.TryGetValue(key, out var valueObj) && valueObj is string valueString)
{
value = this.Serializer.Deserialize<T>(valueString);
value = Serializer.Deserialize<T>(valueString);
return true;
}

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

/// <inheritdoc />
public bool TryDelete(string key)
{
return this.Settings.Values.Remove(key);
return Settings.Values.Remove(key);
}

/// <inheritdoc />
public void Clear()
{
this.Settings.Values.Clear();
Settings.Values.Clear();
}

/// <summary>
Expand All @@ -139,7 +140,7 @@ public void Clear()
/// <returns>True if a value exists.</returns>
public bool KeyExists(string compositeKey, string key)
{
if (this.TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
{
return composite.ContainsKey(key);
}
Expand All @@ -157,12 +158,12 @@ public bool KeyExists(string compositeKey, string key)
/// <returns>The T object.</returns>
public bool TryRead<T>(string compositeKey, string key, out T? value)
{
if (this.TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
{
string compositeValue = (string)composite[key];
if (compositeValue != null)
{
value = this.Serializer.Deserialize<T>(compositeValue);
value = Serializer.Deserialize<T>(compositeValue);
return true;
}
}
Expand All @@ -181,11 +182,11 @@ public bool TryRead<T>(string compositeKey, string key, out T? value)
/// <returns>The T object.</returns>
public T? Read<T>(string compositeKey, string key, T? @default = default)
{
if (this.TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
{
if (composite.TryGetValue(key, out object valueObj) && valueObj is string value)
{
return this.Serializer.Deserialize<T>(value);
return Serializer.Deserialize<T>(value);
}
}

Expand All @@ -202,17 +203,17 @@ public bool TryRead<T>(string compositeKey, string key, out T? value)
/// <param name="values">Objects to save.</param>
public void Save<T>(string compositeKey, IDictionary<string, T> values)
{
if (this.TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
{
foreach (KeyValuePair<string, T> setting in values)
{
if (composite.ContainsKey(setting.Key))
{
composite[setting.Key] = this.Serializer.Serialize(setting.Value);
composite[setting.Key] = Serializer.Serialize(setting.Value);
}
else
{
composite.Add(setting.Key, this.Serializer.Serialize(setting.Value));
composite.Add(setting.Key, Serializer.Serialize(setting.Value));
}
}
}
Expand All @@ -221,10 +222,10 @@ public void Save<T>(string compositeKey, IDictionary<string, T> values)
composite = new ApplicationDataCompositeValue();
foreach (KeyValuePair<string, T> setting in values)
{
composite.Add(setting.Key, this.Serializer.Serialize(setting.Value));
composite.Add(setting.Key, Serializer.Serialize(setting.Value));
}

this.Settings.Values[compositeKey] = composite;
Settings.Values[compositeKey] = composite;
}
}

Expand All @@ -236,7 +237,7 @@ public void Save<T>(string compositeKey, IDictionary<string, T> values)
/// <returns>A boolean indicator of success.</returns>
public bool TryDelete(string compositeKey, string key)
{
if (this.TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
{
return composite.Remove(key);
}
Expand All @@ -247,54 +248,48 @@ public bool TryDelete(string compositeKey, string key)
/// <inheritdoc />
public Task<T?> ReadFileAsync<T>(string filePath, T? @default = default)
{
return this.ReadFileAsync<T>(this.Folder, filePath, @default);
return ReadFileAsync<T>(Folder, filePath, @default);
}

/// <inheritdoc />
public Task<IEnumerable<(DirectoryItemType ItemType, string Name)>> ReadFolderAsync(string folderPath)
{
return this.ReadFolderAsync(this.Folder, folderPath);
return ReadFolderAsync(Folder, folderPath);
}

/// <inheritdoc />
public Task CreateFileAsync<T>(string filePath, T value)
{
return this.SaveFileAsync<T>(this.Folder, filePath, value);
return CreateFileAsync<T>(Folder, filePath, value);
}

/// <inheritdoc />
public Task CreateFolderAsync(string folderPath)
{
return this.CreateFolderAsync(this.Folder, folderPath);
return CreateFolderAsync(Folder, folderPath);
}

/// <inheritdoc />
public Task DeleteItemAsync(string itemPath)
public Task<bool> TryDeleteItemAsync(string itemPath)
{
return this.DeleteItemAsync(this.Folder, itemPath);
return TryDeleteItemAsync(Folder, itemPath);
}

/// <summary>
/// Saves an object inside a file.
/// </summary>
/// <typeparam name="T">Type of object saved.</typeparam>
/// <param name="filePath">Path to the file that will contain the object.</param>
/// <param name="value">Object to save.</param>
/// <returns>Waiting task until completion.</returns>
public Task<StorageFile> SaveFileAsync<T>(string filePath, T value)
/// <inheritdoc />
public Task<bool> TryRenameItemAsync(string itemPath, string newName)
{
return this.SaveFileAsync(this.Folder, filePath, value);
return TryRenameItemAsync(Folder, itemPath, newName);
}

private async Task<T?> ReadFileAsync<T>(StorageFolder folder, string filePath, T? @default = default)
{
string value = await StorageFileHelper.ReadTextFromFileAsync(folder, filePath);
return (value != null) ? this.Serializer.Deserialize<T>(value) : @default;
string value = await StorageFileHelper.ReadTextFromFileAsync(folder, NormalizePath(filePath));
return (value != null) ? Serializer.Deserialize<T>(value) : @default;
}

private async Task<IEnumerable<(DirectoryItemType, string)>> ReadFolderAsync(StorageFolder folder, string folderPath)
{
var targetFolder = await folder.GetFolderAsync(folderPath);
var targetFolder = await folder.GetFolderAsync(NormalizePath(folderPath));
var items = await targetFolder.GetItemsAsync();

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

private Task<StorageFile> SaveFileAsync<T>(StorageFolder folder, string filePath, T value)
private async Task<StorageFile> CreateFileAsync<T>(StorageFolder folder, string filePath, T value)
{
return StorageFileHelper.WriteTextToFileAsync(folder, this.Serializer.Serialize(value)?.ToString(), filePath, CreationCollisionOption.ReplaceExisting);
return await StorageFileHelper.WriteTextToFileAsync(folder, Serializer.Serialize(value)?.ToString(), NormalizePath(filePath), CreationCollisionOption.ReplaceExisting);
}

private async Task CreateFolderAsync(StorageFolder folder, string folderPath)
{
await folder.CreateFolderAsync(folderPath, CreationCollisionOption.OpenIfExists);
await folder.CreateFolderAsync(NormalizePath(folderPath), CreationCollisionOption.OpenIfExists);
}

private async Task<bool> TryDeleteItemAsync(StorageFolder folder, string itemPath)
{
try
{
var item = await folder.GetItemAsync(NormalizePath(itemPath));
await item.DeleteAsync();
return true;
}
catch
{
return false;
}
}

private async Task<bool> TryRenameItemAsync(StorageFolder folder, string itemPath, string newName)
{
try
{
var item = await folder.GetItemAsync(NormalizePath(itemPath));
await item.RenameAsync(newName, NameCollisionOption.FailIfExists);
return true;
}
catch
{
return false;
}
}

private async Task DeleteItemAsync(StorageFolder folder, string itemPath)
private string NormalizePath(string path)
{
var item = await folder.GetItemAsync(itemPath);
await item.DeleteAsync();
return Path.Combine(Path.GetDirectoryName(path), Path.GetFileName(path));
}
}
}
10 changes: 9 additions & 1 deletion Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ public interface IFileStorageHelper
/// </summary>
/// <param name="itemPath">The path to the item for deletion.</param>
/// <returns>Waiting task until completion.</returns>
Task DeleteItemAsync(string itemPath);
Task<bool> TryDeleteItemAsync(string itemPath);

/// <summary>
/// Rename an item.
/// </summary>
/// <param name="itemPath">The path to the target item.</param>
/// <param name="newName">The new nam for the target item.</param>
/// <returns>Waiting task until completion.</returns>
Task<bool> TryRenameItemAsync(string itemPath, string newName);
}
}
Loading

0 comments on commit b605154

Please sign in to comment.