Added app configuration

This commit is contained in:
dan_clark@outlook.com
2022-03-27 19:35:25 +01:00
parent 40d8caf1e0
commit 5ba1e5a63b
17 changed files with 358 additions and 87 deletions
@@ -0,0 +1,13 @@
using System.Reflection;
namespace TheXamlGuy.TaskbarGroup.Core
{
public static class AssemblyExtensions
{
public static Stream ExtractResource(this Assembly assembly, string filename)
{
var resourceName = $"{assembly.GetName().Name.Replace("-", "_")}.{filename}";
return assembly.GetManifestResourceStream(resourceName);
}
}
}
+1
View File
@@ -1,5 +1,6 @@
namespace TheXamlGuy.TaskbarGroup.Core
{
public static class Class1
{
public static void Test(Stream s)
@@ -0,0 +1,12 @@
using Microsoft.Extensions.Configuration;
namespace TheXamlGuy.TaskbarGroup.Core
{
public static class ConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddWritableConfiguration(this IConfigurationBuilder builder)
{
return builder.Add(new WritableJsonConfigurationSource());
}
}
}
@@ -0,0 +1,4 @@
namespace TheXamlGuy.TaskbarGroup.Core
{
public record CreateShortcut(string Path);
}
@@ -0,0 +1,18 @@
using WindowsShortcutFactory;
namespace TheXamlGuy.TaskbarGroup.Core
{
public class CreateShortcutHandler : IMessageHandler<CreateShortcut>
{
public void Handle(CreateShortcut message)
{
using var shortcut = new WindowsShortcut
{
Path = message.Path,
};
shortcut.Save(@"C:\temp\MyShortcut.lnk");
}
}
}
@@ -0,0 +1,13 @@
namespace TheXamlGuy.TaskbarGroup.Core
{
public class ShortcutConfiguration
{
private string? _shortcutDirectory;
public string? ShortcutDirectory
{
get => !string.IsNullOrEmpty(_shortcutDirectory) ? Environment.ExpandEnvironmentVariables(_shortcutDirectory) : null;
set => _shortcutDirectory = value;
}
}
}
@@ -17,8 +17,10 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="System.Reactive" Version="5.0.0" />
<PackageReference Include="System.Runtime.WindowsRuntime" Version="5.0.0-preview.5.20278.1" />
<PackageReference Include="WindowsShortcutFactory" Version="1.0.1" />
</ItemGroup>
<ItemGroup>
<COMReference Include="UIA">
@@ -0,0 +1,73 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.FileProviders;
namespace TheXamlGuy.TaskbarGroup.Core
{
public static class WritableJsonConfigurationExtensions
{
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
string path)
{
return AddWritableJsonFile(builder, null, path, false, false, null);
}
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
string path,
Stream createFromStream)
{
return AddWritableJsonFile(builder, null, path, false, false, createFromStream);
}
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
string path,
bool optional)
{
return AddWritableJsonFile(builder, null, path, optional, false, null);
}
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
string path,
bool optional,
Stream createFromStream)
{
return AddWritableJsonFile(builder, null, path, optional, false, createFromStream);
}
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
string path,
bool optional,
bool reloadOnChange)
{
return AddWritableJsonFile(builder, null, path, optional, reloadOnChange, null);
}
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
string path,
bool optional,
bool reloadOnChange,
Stream createFromStream)
{
return AddWritableJsonFile(builder, null, path, optional, reloadOnChange, createFromStream);
}
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
IFileProvider? provider,
string path,
bool optional,
bool reloadOnChange, Stream? createFromStream)
{
return builder.AddWritableJsonFile(configuration =>
{
configuration.FileProvider = provider;
configuration.Path = path;
configuration.Optional = optional;
configuration.ReloadOnChange = reloadOnChange;
configuration.CreateFromSteam = createFromStream;
configuration.ResolveFileProvider();
});
}
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
Action<WritableJsonConfigurationSource> configureSource) => builder.Add(configureSource);
}
}
@@ -0,0 +1,138 @@
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Text.Json;
namespace TheXamlGuy.TaskbarGroup.Core
{
internal class WritableJsonConfigurationFile
{
private readonly Dictionary<string, (JsonValueKind, string)> _data = new(StringComparer.OrdinalIgnoreCase);
private readonly Stack<string> _paths = new();
private JObject _tokenCache = new();
public IDictionary<string, string> Parse(Stream input)
{
return ParseStream(input);
}
private object ConvertValue(JsonValueKind kind, string value)
{
if (kind is JsonValueKind.True or JsonValueKind.False)
{
return bool.Parse(value);
}
return value;
}
public void Write(string key, string value, Stream output)
{
var tokenPath = $"$.{key.Replace(":", ".")}";
var token = _tokenCache.SelectToken(tokenPath);
if (token is not null)
{
var (kind, _) = _data[key];
var newValue = ConvertValue(kind, value);
_data[key] = new(kind, value);
token.Replace(JToken.FromObject(newValue));
using StreamWriter streamWriter = new(output);
using JsonTextWriter writer = new(streamWriter) { Formatting = Formatting.Indented };
_tokenCache.WriteTo(writer);
output.SetLength(output.Position);
}
}
private void EnterContext(string context)
{
_paths.Push(_paths.Count > 0 ? _paths.Peek() + ConfigurationPath.KeyDelimiter + context : context);
}
private void ExitContext()
{
_paths.Pop();
}
private IDictionary<string, string> ParseStream(Stream input)
{
_data.Clear();
var jsonDocumentOptions = new JsonDocumentOptions
{
CommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true,
};
using (var reader = new StreamReader(input))
{
var content = reader.ReadToEnd();
_tokenCache = JObject.Parse(content);
using var doc = JsonDocument.Parse(content, jsonDocumentOptions);
VisitElement(doc.RootElement);
}
return _data.ToDictionary(k => k.Key, v => v.Value.Item2.ToString());
}
private void VisitElement(JsonElement element)
{
var isEmpty = true;
foreach (JsonProperty property in element.EnumerateObject())
{
isEmpty = false;
EnterContext(property.Name);
VisitValue(property.Value);
ExitContext();
}
if (isEmpty && _paths.Count > 0)
{
_data[_paths.Peek()] = (JsonValueKind.Null, null);
}
}
private void VisitValue(JsonElement value)
{
switch (value.ValueKind)
{
case JsonValueKind.Object:
VisitElement(value);
break;
case JsonValueKind.Array:
int index = 0;
foreach (JsonElement arrayElement in value.EnumerateArray())
{
EnterContext(index.ToString());
VisitValue(arrayElement);
ExitContext();
index++;
}
break;
case JsonValueKind.Number:
case JsonValueKind.String:
case JsonValueKind.True:
case JsonValueKind.False:
case JsonValueKind.Null:
string key = _paths.Peek();
if (_data.ContainsKey(key))
{
throw new FormatException();
}
_data[key] = new(value.ValueKind, value.ToString());
break;
default:
throw new FormatException();
}
}
}
}
@@ -0,0 +1,37 @@
using Microsoft.Extensions.Configuration.Json;
using Microsoft.Extensions.FileProviders;
namespace TheXamlGuy.TaskbarGroup.Core
{
public class WritableJsonConfigurationProvider : JsonConfigurationProvider
{
private readonly WritableJsonConfigurationFile writableJsonConfigurationFile;
public WritableJsonConfigurationProvider(JsonConfigurationSource source) : base(source)
{
writableJsonConfigurationFile = new WritableJsonConfigurationFile();
}
public override void Load(Stream stream)
{
Data = writableJsonConfigurationFile.Parse(stream);
}
public override void Set(string key, string value)
{
var file = Source.FileProvider?.GetFileInfo(Source.Path ?? string.Empty);
static Stream OpenRead(IFileInfo fileInfo)
{
if (fileInfo.PhysicalPath is not null)
{
return new FileStream(fileInfo.PhysicalPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
}
return fileInfo.CreateReadStream();
}
using Stream stream = OpenRead(file);
writableJsonConfigurationFile.Write(key, value, stream);
}
}
}
@@ -0,0 +1,32 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using Microsoft.Extensions.FileProviders;
namespace TheXamlGuy.TaskbarGroup.Core
{
public class WritableJsonConfigurationSource : JsonConfigurationSource
{
public override IConfigurationProvider Build(IConfigurationBuilder builder)
{
EnsureDefaultsWithSteam(builder);
return new WritableJsonConfigurationProvider(this);
}
private void EnsureDefaultsWithSteam(IConfigurationBuilder builder)
{
EnsureDefaults(builder);
if (FileProvider is PhysicalFileProvider physicalFileProvider)
{
var outputFile = System.IO.Path.Combine(physicalFileProvider.Root, Path);
if (!File.Exists(outputFile) && CreateFromSteam is not null)
{
using var fileStream = new FileStream(outputFile, FileMode.Create, FileAccess.Write);
CreateFromSteam.CopyTo(fileStream);
}
}
}
public Stream? CreateFromSteam { get; set; }
}
}