From e180c966ff739c1cdfdee2ceb77f68f2d5770b30 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Fri, 5 Jan 2024 17:34:09 +0000 Subject: [PATCH] Added json configuration --- .../ContextualCommandBuilder.cs | 2 + .../ContextualCommandViewModel.cs | 5 +- .../PrimaryCommandBuilder.cs | 2 + .../PrimaryCommandViewModel.cs | 10 +- Hyperbar.Desktop/App.xaml.cs | 9 +- .../{ => Lifecycles}/AppInitializer.cs | 5 +- .../IServiceCollectionExtensions.cs | 7 +- Hyperbar.Desktop/Templates/TemplateFactory.cs | 3 +- .../Templates/TemplateGeneratorControl.cs | 3 +- Hyperbar.Desktop/Views/CommandViewModel.cs | 4 +- .../Configurations/ConfigurationWriter.cs | 21 +++ .../Configurations/IConfigurationWriter.cs | 6 + .../IWritableConfigurationProvider.cs | 6 + .../IWritableJsonConfigurationBuilder.cs | 12 ++ .../IWritableJsonConfigurationDescriptor.cs | 8 + .../WritableJsonConfigurationBuilder.cs | 110 +++++++++++++ .../WritableJsonConfigurationDescriptor.cs | 3 + .../WritableJsonConfigurationFile.cs | 147 ++++++++++++++++++ .../WritableJsonConfigurationProvider.cs | 43 +++++ .../WritableJsonConfigurationSource.cs | 28 ++++ .../IServiceCollectionExtensions.cs | 24 +-- .../WritableJsonConfigurationExtensions.cs | 78 ++++++++++ Hyperbar/Hyperbar.csproj | 2 + Hyperbar/Lifecycles/AppService.cs | 10 +- Hyperbar/Lifecycles/CommandContext.cs | 3 +- Hyperbar/Lifecycles/ICommandBuilder.cs | 2 +- Hyperbar/Lifecycles/ICommandContext.cs | 2 +- Hyperbar/Lifecycles/ICommandViewModel.cs | 2 +- Hyperbar/Lifecycles/IInitializer.cs | 2 +- Hyperbar/Templates/DataTemplateDescriptor.cs | 2 +- Hyperbar/Templates/IDataTemplateDescriptor.cs | 2 +- Hyperbar/Templates/ITemplateFactory.cs | 2 +- Hyperbar/Templates/ITemplatedViewModel.cs | 2 +- 33 files changed, 527 insertions(+), 40 deletions(-) rename Hyperbar.Desktop/{ => Lifecycles}/AppInitializer.cs (69%) rename Hyperbar.Desktop/{Templates => Lifecycles}/IServiceCollectionExtensions.cs (76%) create mode 100644 Hyperbar/Configurations/ConfigurationWriter.cs create mode 100644 Hyperbar/Configurations/IConfigurationWriter.cs create mode 100644 Hyperbar/Configurations/IWritableConfigurationProvider.cs create mode 100644 Hyperbar/Configurations/IWritableJsonConfigurationBuilder.cs create mode 100644 Hyperbar/Configurations/IWritableJsonConfigurationDescriptor.cs create mode 100644 Hyperbar/Configurations/WritableJsonConfigurationBuilder.cs create mode 100644 Hyperbar/Configurations/WritableJsonConfigurationDescriptor.cs create mode 100644 Hyperbar/Configurations/WritableJsonConfigurationFile.cs create mode 100644 Hyperbar/Configurations/WritableJsonConfigurationProvider.cs create mode 100644 Hyperbar/Configurations/WritableJsonConfigurationSource.cs rename Hyperbar/{Lifecycles => Extensions}/IServiceCollectionExtensions.cs (56%) create mode 100644 Hyperbar/Extensions/WritableJsonConfigurationExtensions.cs diff --git a/Hyperbar.Desktop.Contextual/ContextualCommandBuilder.cs b/Hyperbar.Desktop.Contextual/ContextualCommandBuilder.cs index e53992f..4bb13e7 100644 --- a/Hyperbar.Desktop.Contextual/ContextualCommandBuilder.cs +++ b/Hyperbar.Desktop.Contextual/ContextualCommandBuilder.cs @@ -1,3 +1,5 @@ +using Hyperbar.Extensions; +using Hyperbar.Lifecycles; using Microsoft.Extensions.DependencyInjection; namespace Hyperbar.Desktop.Contextual; diff --git a/Hyperbar.Desktop.Contextual/ContextualCommandViewModel.cs b/Hyperbar.Desktop.Contextual/ContextualCommandViewModel.cs index 61c5371..08e3e99 100644 --- a/Hyperbar.Desktop.Contextual/ContextualCommandViewModel.cs +++ b/Hyperbar.Desktop.Contextual/ContextualCommandViewModel.cs @@ -1,4 +1,7 @@ -namespace Hyperbar.Desktop.Contextual; +using Hyperbar.Lifecycles; +using Hyperbar.Templates; + +namespace Hyperbar.Desktop.Contextual; public class ContextualCommandViewModel(ITemplateFactory templateFactory) : ICommandViewModel, diff --git a/Hyperbar.Desktop.Primary/PrimaryCommandBuilder.cs b/Hyperbar.Desktop.Primary/PrimaryCommandBuilder.cs index 80ae121..60fd2c5 100644 --- a/Hyperbar.Desktop.Primary/PrimaryCommandBuilder.cs +++ b/Hyperbar.Desktop.Primary/PrimaryCommandBuilder.cs @@ -1,3 +1,5 @@ +using Hyperbar.Extensions; +using Hyperbar.Lifecycles; using Microsoft.Extensions.DependencyInjection; namespace Hyperbar.Desktop.Primary; diff --git a/Hyperbar.Desktop.Primary/PrimaryCommandViewModel.cs b/Hyperbar.Desktop.Primary/PrimaryCommandViewModel.cs index 7eaa290..b9263fb 100644 --- a/Hyperbar.Desktop.Primary/PrimaryCommandViewModel.cs +++ b/Hyperbar.Desktop.Primary/PrimaryCommandViewModel.cs @@ -1,4 +1,12 @@ -namespace Hyperbar.Desktop.Primary; +using Hyperbar.Lifecycles; +using Hyperbar.Templates; + +namespace Hyperbar.Desktop.Primary; + +public class PrimaryCommandConfiguration +{ + +} public class PrimaryCommandViewModel(ITemplateFactory templateFactory) : ICommandViewModel, diff --git a/Hyperbar.Desktop/App.xaml.cs b/Hyperbar.Desktop/App.xaml.cs index 09b6058..38edcc0 100644 --- a/Hyperbar.Desktop/App.xaml.cs +++ b/Hyperbar.Desktop/App.xaml.cs @@ -1,6 +1,8 @@ using Hyperbar.Desktop.Contextual; using Hyperbar.Desktop.Controls; using Hyperbar.Desktop.Primary; +using Hyperbar.Lifecycles; +using Hyperbar.Templates; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.UI.Xaml; @@ -30,11 +32,10 @@ public partial class App : services.AddTransient(); services.AddTransient(); - services.AddDataTemplate("Commands"); + services.AddDataTemplate(); - // Commands - services.AddSomething(); - services.AddSomething(); + services.AddCommandBuilder("Contexual.Commands"); + services.AddCommandBuilder("Primary.Command"); services.AddTransient(provider => { diff --git a/Hyperbar.Desktop/AppInitializer.cs b/Hyperbar.Desktop/Lifecycles/AppInitializer.cs similarity index 69% rename from Hyperbar.Desktop/AppInitializer.cs rename to Hyperbar.Desktop/Lifecycles/AppInitializer.cs index a2156f2..a289ac8 100644 --- a/Hyperbar.Desktop/AppInitializer.cs +++ b/Hyperbar.Desktop/Lifecycles/AppInitializer.cs @@ -1,11 +1,12 @@ using Hyperbar.Desktop.Controls; +using Hyperbar.Lifecycles; using Microsoft.Extensions.DependencyInjection; using System.Threading.Tasks; namespace Hyperbar.Desktop; -public class AppInitializer([FromKeyedServices("Commands")] CommandView view, - [FromKeyedServices("Commands")] CommandViewModel viewModel, +public class AppInitializer([FromKeyedServices(nameof(CommandView))] CommandView view, + [FromKeyedServices(nameof(CommandView))] CommandViewModel viewModel, DesktopFlyout desktopFlyout) : IInitializer { diff --git a/Hyperbar.Desktop/Templates/IServiceCollectionExtensions.cs b/Hyperbar.Desktop/Lifecycles/IServiceCollectionExtensions.cs similarity index 76% rename from Hyperbar.Desktop/Templates/IServiceCollectionExtensions.cs rename to Hyperbar.Desktop/Lifecycles/IServiceCollectionExtensions.cs index a0b8a37..b588995 100644 --- a/Hyperbar.Desktop/Templates/IServiceCollectionExtensions.cs +++ b/Hyperbar.Desktop/Lifecycles/IServiceCollectionExtensions.cs @@ -1,11 +1,14 @@ -using Microsoft.Extensions.DependencyInjection; +using Hyperbar.Lifecycles; +using Hyperbar.Templates; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace Hyperbar.Desktop { public static class IServiceCollectionExtensions { - public static IServiceCollection AddSomething(this IServiceCollection services) + public static IServiceCollection AddCommandBuilder(this IServiceCollection services, + string key) where TCommandBuilder : ICommandBuilder, new() { diff --git a/Hyperbar.Desktop/Templates/TemplateFactory.cs b/Hyperbar.Desktop/Templates/TemplateFactory.cs index ed71211..17f4dc5 100644 --- a/Hyperbar.Desktop/Templates/TemplateFactory.cs +++ b/Hyperbar.Desktop/Templates/TemplateFactory.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using Hyperbar.Templates; +using Microsoft.Extensions.DependencyInjection; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using System; diff --git a/Hyperbar.Desktop/Templates/TemplateGeneratorControl.cs b/Hyperbar.Desktop/Templates/TemplateGeneratorControl.cs index 10dda65..d8b22ce 100644 --- a/Hyperbar.Desktop/Templates/TemplateGeneratorControl.cs +++ b/Hyperbar.Desktop/Templates/TemplateGeneratorControl.cs @@ -1,4 +1,5 @@ -using Microsoft.UI.Xaml; +using Hyperbar.Templates; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; namespace Hyperbar.Desktop; diff --git a/Hyperbar.Desktop/Views/CommandViewModel.cs b/Hyperbar.Desktop/Views/CommandViewModel.cs index b88fb50..b0125e6 100644 --- a/Hyperbar.Desktop/Views/CommandViewModel.cs +++ b/Hyperbar.Desktop/Views/CommandViewModel.cs @@ -1,4 +1,6 @@ -using System.Collections; +using Hyperbar.Lifecycles; +using Hyperbar.Templates; +using System.Collections; using System.Collections.Generic; namespace Hyperbar.Desktop; diff --git a/Hyperbar/Configurations/ConfigurationWriter.cs b/Hyperbar/Configurations/ConfigurationWriter.cs new file mode 100644 index 0000000..8d42110 --- /dev/null +++ b/Hyperbar/Configurations/ConfigurationWriter.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.Configuration; + +namespace Hyperbar.Configurations; + +public class ConfigurationWriter(IConfiguration rootConfiguration) : + IConfigurationWriter where TConfiguration : class, new() +{ + public void Write(string section, TConfiguration configuration) + { + if (rootConfiguration is IConfigurationRoot root) + { + foreach (IConfigurationProvider? provider in root.Providers) + { + if (provider is IWritableConfigurationProvider writableConfigurationProvider) + { + writableConfigurationProvider.Write(section, configuration); + } + } + } + } +} \ No newline at end of file diff --git a/Hyperbar/Configurations/IConfigurationWriter.cs b/Hyperbar/Configurations/IConfigurationWriter.cs new file mode 100644 index 0000000..b2f414b --- /dev/null +++ b/Hyperbar/Configurations/IConfigurationWriter.cs @@ -0,0 +1,6 @@ +namespace Hyperbar.Configurations; + +public interface IConfigurationWriter where TConfiguration : class +{ + void Write(string section, TConfiguration args); +} diff --git a/Hyperbar/Configurations/IWritableConfigurationProvider.cs b/Hyperbar/Configurations/IWritableConfigurationProvider.cs new file mode 100644 index 0000000..f7a7cdb --- /dev/null +++ b/Hyperbar/Configurations/IWritableConfigurationProvider.cs @@ -0,0 +1,6 @@ +namespace Hyperbar.Configurations; + +public interface IWritableConfigurationProvider +{ + void Write(string section, TValue value) where TValue : class, new(); +} diff --git a/Hyperbar/Configurations/IWritableJsonConfigurationBuilder.cs b/Hyperbar/Configurations/IWritableJsonConfigurationBuilder.cs new file mode 100644 index 0000000..2dd777c --- /dev/null +++ b/Hyperbar/Configurations/IWritableJsonConfigurationBuilder.cs @@ -0,0 +1,12 @@ +namespace Hyperbar.Configurations; + +public interface IWritableJsonConfigurationBuilder +{ + Stream? DefaultFileStream { get; } + + IWritableJsonConfigurationBuilder AddDefaultConfiguration(string Key) where TConfiguration : class; + + IWritableJsonConfigurationBuilder AddDefaultFileStream(Stream stream); + + void Build(string path); +} diff --git a/Hyperbar/Configurations/IWritableJsonConfigurationDescriptor.cs b/Hyperbar/Configurations/IWritableJsonConfigurationDescriptor.cs new file mode 100644 index 0000000..8e66414 --- /dev/null +++ b/Hyperbar/Configurations/IWritableJsonConfigurationDescriptor.cs @@ -0,0 +1,8 @@ +namespace Hyperbar.Configurations; + +public interface IWritableJsonConfigurationDescriptor +{ + Type ConfigurationType { get; } + + string Key { get; } +} diff --git a/Hyperbar/Configurations/WritableJsonConfigurationBuilder.cs b/Hyperbar/Configurations/WritableJsonConfigurationBuilder.cs new file mode 100644 index 0000000..8c84ce6 --- /dev/null +++ b/Hyperbar/Configurations/WritableJsonConfigurationBuilder.cs @@ -0,0 +1,110 @@ +using Json.Patch; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using JsonSerializer = System.Text.Json.JsonSerializer; + +namespace Hyperbar.Configurations; + +public class WritableJsonConfigurationBuilder : + IWritableJsonConfigurationBuilder +{ + private readonly List descriptors = []; + + public Stream? DefaultFileStream { get; private set; } + + public IReadOnlyCollection Descriptors => new ReadOnlyCollection(descriptors); + + public IWritableJsonConfigurationBuilder AddDefaultConfiguration(string Key) where TConfiguration : class + { + descriptors.Add(new WritableJsonConfigurationDescriptor(typeof(TConfiguration), Key)); + return this; + } + + public IWritableJsonConfigurationBuilder AddDefaultFileStream(Stream stream) + { + DefaultFileStream = stream; + return this; + } + + public void Build(string path) + { + JsonSerializerOptions options = new() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; + + JObject? sourceDocument = []; + if (TryLoadSource(out string? defaultContent)) + { + sourceDocument = JObject.Parse(defaultContent!); + } + + JObject? targetDocument = []; + if (TryLoadTarget(path, out string? targetContent)) + { + targetDocument = JObject.Parse(targetContent!); + } + + foreach (IWritableJsonConfigurationDescriptor? descriptor in descriptors) + { + if (sourceDocument.SelectToken($"$.{descriptor.Key}") is JToken sourceSection) + { + if (targetDocument.SelectToken($"$.{descriptor.Key}") is JToken targetSection) + { + object? source = JsonSerializer.Deserialize(JsonConvert.SerializeObject(sourceSection), descriptor.ConfigurationType); + object? target = JsonSerializer.Deserialize(JsonConvert.SerializeObject(targetSection), descriptor.ConfigurationType); + + JsonPatch? patch = source.CreatePatch(target); + if (patch.Apply(source) is object sourcePatched) + { + targetSection.Replace(JToken.Parse(JsonSerializer.Serialize(sourcePatched, options))); + } + } + else + { + object? source = JsonSerializer.Deserialize(JsonConvert.SerializeObject(sourceSection), descriptor.ConfigurationType); + targetDocument.Add(descriptor.Key, JToken.Parse(JsonSerializer.Serialize(source, options))); + } + } + else + { + object? configuration = Activator.CreateInstance(descriptor.ConfigurationType); + targetDocument.Add(descriptor.Key, JToken.Parse(JsonSerializer.Serialize(configuration, options))); + } + } + + using FileStream? fileStream = new(path, FileMode.Create, FileAccess.Write); + using StreamWriter streamWriter = new(fileStream); + using JsonTextWriter writer = new(streamWriter) { Formatting = Formatting.Indented }; + targetDocument.WriteTo(writer); + } + + private static bool TryLoadTarget(string path, [MaybeNull] out string? content) + { + if (File.Exists(path)) + { + using FileStream? fileStream = new(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using StreamReader? streamReader = new(fileStream); + content = streamReader.ReadToEnd(); + + return true; + } + + content = null; + return false; + } + + private bool TryLoadSource([MaybeNull] out string? content) + { + if (DefaultFileStream is Stream fileStream) + { + using StreamReader? streamReader = new(fileStream); + content = streamReader.ReadToEnd(); + return true; + } + + content = null; + return false; + } +} diff --git a/Hyperbar/Configurations/WritableJsonConfigurationDescriptor.cs b/Hyperbar/Configurations/WritableJsonConfigurationDescriptor.cs new file mode 100644 index 0000000..8b274c2 --- /dev/null +++ b/Hyperbar/Configurations/WritableJsonConfigurationDescriptor.cs @@ -0,0 +1,3 @@ +namespace Hyperbar.Configurations; + +public record WritableJsonConfigurationDescriptor(Type ConfigurationType, string Key) : IWritableJsonConfigurationDescriptor; diff --git a/Hyperbar/Configurations/WritableJsonConfigurationFile.cs b/Hyperbar/Configurations/WritableJsonConfigurationFile.cs new file mode 100644 index 0000000..1c14ced --- /dev/null +++ b/Hyperbar/Configurations/WritableJsonConfigurationFile.cs @@ -0,0 +1,147 @@ +using Microsoft.Extensions.Configuration; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Text.Json; + +namespace Hyperbar.Configurations; +public class WritableJsonConfigurationFile +{ + private readonly Dictionary data = new(StringComparer.OrdinalIgnoreCase); + private readonly Stack paths = new(); + private JObject tokenCache = []; + private bool isParsing; + + public IDictionary Parse(Stream input) + { + return ParseStream(input); + } + + public void Write(string key, string value, Stream output) + { + if (isParsing) + { + return; + } + + if (key[^1] == ':') + { + key = key[0..^1]; + } + + string? tokenPath = $"$.{key.Replace(":", ".")}"; + key = key.Replace("[", "").Replace("]", ""); + + if (tokenCache.SelectToken(tokenPath) is JToken token && data.ContainsKey(key)) + { + (JsonValueKind? kind, string _) = data[key]; + object? 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 static object ConvertValue(JsonValueKind? kind, string value) + { + return kind is JsonValueKind.True or JsonValueKind.False ? bool.Parse(value) : value; + } + + private void EnterContext(string context) + { + paths.Push(paths.Count > 0 ? paths.Peek() + ConfigurationPath.KeyDelimiter + context : context); + } + + private void ExitContext() + { + _ = paths.Pop(); + } + + private IDictionary ParseStream(Stream input) + { + data.Clear(); + + JsonDocumentOptions jsonDocumentOptions = new() + { + CommentHandling = JsonCommentHandling.Skip, + AllowTrailingCommas = true, + }; + + isParsing = true; + using (StreamReader? reader = new(input)) + { + string? content = reader.ReadToEnd(); + tokenCache = JObject.Parse(content); + + using JsonDocument? doc = JsonDocument.Parse(content, jsonDocumentOptions); + VisitElement(doc.RootElement); + } + isParsing = false; + + return data.ToDictionary(k => k.Key, v => v.Value.Item2?.ToString() ?? null); + } + + private void VisitElement(JsonElement element) + { + bool 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; + + case JsonValueKind.Undefined: + break; + + default: + throw new FormatException(); + } + } +} \ No newline at end of file diff --git a/Hyperbar/Configurations/WritableJsonConfigurationProvider.cs b/Hyperbar/Configurations/WritableJsonConfigurationProvider.cs new file mode 100644 index 0000000..6edfb0f --- /dev/null +++ b/Hyperbar/Configurations/WritableJsonConfigurationProvider.cs @@ -0,0 +1,43 @@ +using Microsoft.Extensions.Configuration.Json; +using Microsoft.Extensions.FileProviders; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Hyperbar.Configurations; + +public class WritableJsonConfigurationProvider(JsonConfigurationSource source) : + JsonConfigurationProvider(source), + IWritableConfigurationProvider +{ + public void Write(string section, TValue value) where TValue : class, new() + { + IFileInfo? file = Source.FileProvider?.GetFileInfo(Source.Path ?? string.Empty); + static Stream OpenRead(IFileInfo fileInfo) + { + return fileInfo.PhysicalPath is not null + ? new FileStream(fileInfo.PhysicalPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite) + : fileInfo.CreateReadStream(); + } + + if (file is null) + { + throw new FileNotFoundException(); + } + + using Stream stream = OpenRead(file); + using StreamReader? reader = new(stream); + + string? content = reader.ReadToEnd(); + JObject? document = JObject.Parse(content); + + if (document.SelectToken($"$.{section}") is JToken sectionToken) + { + sectionToken.Replace(JToken.Parse(JsonConvert.SerializeObject(value))); + stream.SetLength(0); + + using StreamWriter streamWriter = new(stream); + using JsonTextWriter writer = new(streamWriter) { Formatting = Formatting.Indented }; + document.WriteTo(writer); + } + } +} diff --git a/Hyperbar/Configurations/WritableJsonConfigurationSource.cs b/Hyperbar/Configurations/WritableJsonConfigurationSource.cs new file mode 100644 index 0000000..a12f105 --- /dev/null +++ b/Hyperbar/Configurations/WritableJsonConfigurationSource.cs @@ -0,0 +1,28 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.Json; +using Microsoft.Extensions.FileProviders; + +namespace Hyperbar.Configurations; + +public class WritableJsonConfigurationSource : + JsonConfigurationSource +{ + public IWritableJsonConfigurationBuilder? Factory { get; set; } + + public override IConfigurationProvider Build(IConfigurationBuilder builder) + { + EnsureDefaultsWithSteam(builder); + return new WritableJsonConfigurationProvider(this); + } + + private void EnsureDefaultsWithSteam(IConfigurationBuilder builder) + { + EnsureDefaults(builder); + + if (FileProvider is PhysicalFileProvider physicalFileProvider) + { + string? outputFile = System.IO.Path.Combine(physicalFileProvider.Root, Path); + Factory?.Build(outputFile); + } + } +} diff --git a/Hyperbar/Lifecycles/IServiceCollectionExtensions.cs b/Hyperbar/Extensions/IServiceCollectionExtensions.cs similarity index 56% rename from Hyperbar/Lifecycles/IServiceCollectionExtensions.cs rename to Hyperbar/Extensions/IServiceCollectionExtensions.cs index 1a4c737..0b888c1 100644 --- a/Hyperbar/Lifecycles/IServiceCollectionExtensions.cs +++ b/Hyperbar/Extensions/IServiceCollectionExtensions.cs @@ -1,8 +1,8 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; +using Hyperbar.Lifecycles; +using Hyperbar.Templates; +using Microsoft.Extensions.DependencyInjection; namespace Hyperbar; - public static class IServiceCollectionExtensions { public static IServiceCollection AddCommandTemplate(this IServiceCollection services) @@ -12,15 +12,15 @@ public static class IServiceCollectionExtensions Type dataType = typeof(TCommand); Type templateType = typeof(TCommandTemplate); - var key = dataType.Name; + string key = dataType.Name; - services.AddTransient(typeof(ICommandViewModel), dataType); - services.AddTransient(templateType); + _ = services.AddTransient(typeof(ICommandViewModel), dataType); + _ = services.AddTransient(templateType); - services.AddKeyedTransient(typeof(ICommandViewModel), key, dataType); - services.AddKeyedTransient(templateType, key); + _ = services.AddKeyedTransient(typeof(ICommandViewModel), key, dataType); + _ = services.AddKeyedTransient(templateType, key); - services.AddTransient(provider => new DataTemplateDescriptor + _ = services.AddTransient(provider => new DataTemplateDescriptor { DataType = dataType, TemplateType = templateType, @@ -38,10 +38,10 @@ public static class IServiceCollectionExtensions key ??= dataType.Name; - services.AddKeyedTransient(dataType, key); - services.AddKeyedTransient(templateType, key); + _ = services.AddKeyedTransient(dataType, key); + _ = services.AddKeyedTransient(templateType, key); - services.AddTransient(provider => new DataTemplateDescriptor + _ = services.AddTransient(provider => new DataTemplateDescriptor { DataType = dataType, TemplateType = templateType, diff --git a/Hyperbar/Extensions/WritableJsonConfigurationExtensions.cs b/Hyperbar/Extensions/WritableJsonConfigurationExtensions.cs new file mode 100644 index 0000000..edff984 --- /dev/null +++ b/Hyperbar/Extensions/WritableJsonConfigurationExtensions.cs @@ -0,0 +1,78 @@ +using Hyperbar.Configurations; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.FileProviders; + +namespace Hyperbar.Extensions; + +public static class WritableJsonConfigurationExtensions +{ + public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder, + string path) + { + return builder.AddWritableJsonFile(null, path, false, false, null); + } + + public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder, + string path, + Action? factoryDelegate) + { + return builder.AddWritableJsonFile(null, path, false, false, factoryDelegate); + } + + public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder, + string path, + bool optional) + { + return builder.AddWritableJsonFile(null, path, optional, false, null); + } + + public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder, + string path, + bool optional, + Action? factoryDelegate) + { + return builder.AddWritableJsonFile(null, path, optional, false, factoryDelegate); + } + + public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder, + string path, + bool optional, + bool reloadOnChange) + { + return builder.AddWritableJsonFile(null, path, optional, reloadOnChange, null); + } + + public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder, + string path, + bool optional, + bool reloadOnChange, + Action? factoryDelegate) + { + return builder.AddWritableJsonFile(null, path, optional, reloadOnChange, factoryDelegate); + } + + public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder, + IFileProvider? provider, + string path, + bool optional, + bool reloadOnChange, Action? writableJsonConfigurationDelegate) + { + IWritableJsonConfigurationBuilder writableJsonConfigurationBuilder = new WritableJsonConfigurationBuilder(); + writableJsonConfigurationDelegate?.Invoke(writableJsonConfigurationBuilder); + + return builder.AddWritableJsonFile(configuration => + { + configuration.FileProvider = provider; + configuration.Path = path; + configuration.Optional = optional; + configuration.ReloadOnChange = reloadOnChange; + configuration.Factory = writableJsonConfigurationBuilder; + configuration.ResolveFileProvider(); + }); + } + + public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder, Action configureSource) + { + return builder.Add(configureSource); + } +} diff --git a/Hyperbar/Hyperbar.csproj b/Hyperbar/Hyperbar.csproj index 26d19f6..d638b86 100644 --- a/Hyperbar/Hyperbar.csproj +++ b/Hyperbar/Hyperbar.csproj @@ -6,5 +6,7 @@ + + diff --git a/Hyperbar/Lifecycles/AppService.cs b/Hyperbar/Lifecycles/AppService.cs index db87bcb..a18247e 100644 --- a/Hyperbar/Lifecycles/AppService.cs +++ b/Hyperbar/Lifecycles/AppService.cs @@ -1,20 +1,20 @@ using Microsoft.Extensions.Hosting; using System.Collections.ObjectModel; -namespace Hyperbar; +namespace Hyperbar.Lifecycles; -public class ObservableCollectionViewModel : +public class ObservableCollectionViewModel : ObservableCollection { } -public class AppService(IEnumerable initializers) : +public class AppService(IEnumerable initializers) : IHostedService { public async Task StartAsync(CancellationToken cancellationToken) { - foreach (var initializer in initializers) + foreach (IInitializer initializer in initializers) { await initializer.InitializeAsync(); } @@ -22,6 +22,6 @@ public class AppService(IEnumerable initializers) : public Task StopAsync(CancellationToken cancellationToken) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } } diff --git a/Hyperbar/Lifecycles/CommandContext.cs b/Hyperbar/Lifecycles/CommandContext.cs index 029c0de..0dbf143 100644 --- a/Hyperbar/Lifecycles/CommandContext.cs +++ b/Hyperbar/Lifecycles/CommandContext.cs @@ -1,5 +1,4 @@ - -namespace Hyperbar; +namespace Hyperbar.Lifecycles; public class CommandContext(IServiceProvider serviceProvider) : ICommandContext diff --git a/Hyperbar/Lifecycles/ICommandBuilder.cs b/Hyperbar/Lifecycles/ICommandBuilder.cs index 1954cb0..601e8d7 100644 --- a/Hyperbar/Lifecycles/ICommandBuilder.cs +++ b/Hyperbar/Lifecycles/ICommandBuilder.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.DependencyInjection; -namespace Hyperbar; +namespace Hyperbar.Lifecycles; public interface ICommandBuilder { diff --git a/Hyperbar/Lifecycles/ICommandContext.cs b/Hyperbar/Lifecycles/ICommandContext.cs index 05422be..50f8ea3 100644 --- a/Hyperbar/Lifecycles/ICommandContext.cs +++ b/Hyperbar/Lifecycles/ICommandContext.cs @@ -1,4 +1,4 @@ -namespace Hyperbar; +namespace Hyperbar.Lifecycles; public interface ICommandContext { diff --git a/Hyperbar/Lifecycles/ICommandViewModel.cs b/Hyperbar/Lifecycles/ICommandViewModel.cs index 747fda2..fe4dac7 100644 --- a/Hyperbar/Lifecycles/ICommandViewModel.cs +++ b/Hyperbar/Lifecycles/ICommandViewModel.cs @@ -1,4 +1,4 @@ -namespace Hyperbar; +namespace Hyperbar.Lifecycles; public interface ICommandViewModel { diff --git a/Hyperbar/Lifecycles/IInitializer.cs b/Hyperbar/Lifecycles/IInitializer.cs index 57714a1..4233207 100644 --- a/Hyperbar/Lifecycles/IInitializer.cs +++ b/Hyperbar/Lifecycles/IInitializer.cs @@ -1,4 +1,4 @@ -namespace Hyperbar; +namespace Hyperbar.Lifecycles; public interface IInitializer { diff --git a/Hyperbar/Templates/DataTemplateDescriptor.cs b/Hyperbar/Templates/DataTemplateDescriptor.cs index 65c9812..8e84f0e 100644 --- a/Hyperbar/Templates/DataTemplateDescriptor.cs +++ b/Hyperbar/Templates/DataTemplateDescriptor.cs @@ -1,4 +1,4 @@ -namespace Hyperbar; +namespace Hyperbar.Templates; public record DataTemplateDescriptor : IDataTemplateDescriptor diff --git a/Hyperbar/Templates/IDataTemplateDescriptor.cs b/Hyperbar/Templates/IDataTemplateDescriptor.cs index 88495ec..7f2a562 100644 --- a/Hyperbar/Templates/IDataTemplateDescriptor.cs +++ b/Hyperbar/Templates/IDataTemplateDescriptor.cs @@ -1,4 +1,4 @@ -namespace Hyperbar; +namespace Hyperbar.Templates; public interface IDataTemplateDescriptor { diff --git a/Hyperbar/Templates/ITemplateFactory.cs b/Hyperbar/Templates/ITemplateFactory.cs index 73bf234..db5d881 100644 --- a/Hyperbar/Templates/ITemplateFactory.cs +++ b/Hyperbar/Templates/ITemplateFactory.cs @@ -1,4 +1,4 @@ -namespace Hyperbar; +namespace Hyperbar.Templates; public interface ITemplateFactory { diff --git a/Hyperbar/Templates/ITemplatedViewModel.cs b/Hyperbar/Templates/ITemplatedViewModel.cs index 04a61f1..d98d0f8 100644 --- a/Hyperbar/Templates/ITemplatedViewModel.cs +++ b/Hyperbar/Templates/ITemplatedViewModel.cs @@ -1,4 +1,4 @@ -namespace Hyperbar; +namespace Hyperbar.Templates; public interface ITemplatedViewModel {