diff --git a/Hyperbar.Desktop.Contextual/ContextualCommandBuilder.cs b/Hyperbar.Desktop.Contextual/ContextualCommandBuilder.cs
index 4bb13e7..0d1c942 100644
--- a/Hyperbar.Desktop.Contextual/ContextualCommandBuilder.cs
+++ b/Hyperbar.Desktop.Contextual/ContextualCommandBuilder.cs
@@ -1,4 +1,3 @@
-using Hyperbar.Extensions;
using Hyperbar.Lifecycles;
using Microsoft.Extensions.DependencyInjection;
diff --git a/Hyperbar.Desktop.Contextual/Hyperbar.Desktop.Contextual.csproj b/Hyperbar.Desktop.Contextual/Hyperbar.Desktop.Contextual.csproj
index f238d6e..15d233c 100644
--- a/Hyperbar.Desktop.Contextual/Hyperbar.Desktop.Contextual.csproj
+++ b/Hyperbar.Desktop.Contextual/Hyperbar.Desktop.Contextual.csproj
@@ -7,9 +7,6 @@
true
true
-
-
-
@@ -19,9 +16,4 @@
-
-
- MSBuild:Compile
-
-
diff --git a/Hyperbar.Desktop.Primary/Hyperbar.Desktop.Primary.csproj b/Hyperbar.Desktop.Primary/Hyperbar.Desktop.Primary.csproj
index d00105c..ebdcde3 100644
--- a/Hyperbar.Desktop.Primary/Hyperbar.Desktop.Primary.csproj
+++ b/Hyperbar.Desktop.Primary/Hyperbar.Desktop.Primary.csproj
@@ -7,9 +7,14 @@
true
true
-
-
-
+
+ True
+ True
+
+
+ True
+ True
+
@@ -18,9 +23,4 @@
-
-
- MSBuild:Compile
-
-
diff --git a/Hyperbar.Desktop.Primary/PrimaryCommandBuilder.cs b/Hyperbar.Desktop.Primary/PrimaryCommandBuilder.cs
index 60fd2c5..af612f9 100644
--- a/Hyperbar.Desktop.Primary/PrimaryCommandBuilder.cs
+++ b/Hyperbar.Desktop.Primary/PrimaryCommandBuilder.cs
@@ -1,4 +1,3 @@
-using Hyperbar.Extensions;
using Hyperbar.Lifecycles;
using Microsoft.Extensions.DependencyInjection;
diff --git a/Hyperbar.Desktop/App.xaml.cs b/Hyperbar.Desktop/App.xaml.cs
index 38edcc0..3c9e866 100644
--- a/Hyperbar.Desktop/App.xaml.cs
+++ b/Hyperbar.Desktop/App.xaml.cs
@@ -3,6 +3,7 @@ using Hyperbar.Desktop.Controls;
using Hyperbar.Desktop.Primary;
using Hyperbar.Lifecycles;
using Hyperbar.Templates;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.UI.Xaml;
@@ -11,6 +12,10 @@ using System.Collections.Generic;
namespace Hyperbar.Desktop;
+public class AppConfiguration
+{
+
+}
public partial class App :
Application
{
@@ -22,7 +27,14 @@ public partial class App :
IHost? host = Host.CreateDefaultBuilder()
.UseContentRoot(AppContext.BaseDirectory)
- .ConfigureServices(services =>
+ .ConfigureAppConfiguration(config =>
+ {
+ config.SetBasePath(AppContext.BaseDirectory);
+ config.AddJsonFile("Settings.json", true);
+
+ config.Build();
+ })
+ .ConfigureServices((context, services) =>
{
services.AddHostedService();
@@ -34,8 +46,8 @@ public partial class App :
services.AddDataTemplate();
- services.AddCommandBuilder("Contexual.Commands");
- services.AddCommandBuilder("Primary.Command");
+ services.AddCommand("");
+ services.AddCommand("");
services.AddTransient(provider =>
{
@@ -53,9 +65,11 @@ public partial class App :
return Resolve(provider);
});
+
+ services.ConfigureWritableOptions();
+
})
.Build();
-
await host.RunAsync();
}
}
diff --git a/Hyperbar.Desktop/Hyperbar.Desktop.csproj b/Hyperbar.Desktop/Hyperbar.Desktop.csproj
index 44c7805..65df773 100644
--- a/Hyperbar.Desktop/Hyperbar.Desktop.csproj
+++ b/Hyperbar.Desktop/Hyperbar.Desktop.csproj
@@ -14,8 +14,8 @@
enable
-
-
+
+
@@ -36,27 +36,12 @@
-
- MSBuild:Compile
-
-
-
-
-
-
-
-
-
-
- MSBuild:Compile
-
-
-
-
- MSBuild:Compile
-
+
+
+
+
true
-
+
\ No newline at end of file
diff --git a/Hyperbar.Desktop/Lifecycles/AppInitializer.cs b/Hyperbar.Desktop/Lifecycles/AppInitializer.cs
index a289ac8..aff4d1f 100644
--- a/Hyperbar.Desktop/Lifecycles/AppInitializer.cs
+++ b/Hyperbar.Desktop/Lifecycles/AppInitializer.cs
@@ -5,8 +5,8 @@ using System.Threading.Tasks;
namespace Hyperbar.Desktop;
-public class AppInitializer([FromKeyedServices(nameof(CommandView))] CommandView view,
- [FromKeyedServices(nameof(CommandView))] CommandViewModel viewModel,
+public class AppInitializer([FromKeyedServices(nameof(CommandViewModel))] CommandView view,
+ [FromKeyedServices(nameof(CommandViewModel))] CommandViewModel viewModel,
DesktopFlyout desktopFlyout) :
IInitializer
{
diff --git a/Hyperbar.Desktop/Lifecycles/IServiceCollectionExtensions.cs b/Hyperbar.Desktop/Lifecycles/IServiceCollectionExtensions.cs
index b588995..cb3c62d 100644
--- a/Hyperbar.Desktop/Lifecycles/IServiceCollectionExtensions.cs
+++ b/Hyperbar.Desktop/Lifecycles/IServiceCollectionExtensions.cs
@@ -7,19 +7,19 @@ namespace Hyperbar.Desktop
{
public static class IServiceCollectionExtensions
{
- public static IServiceCollection AddCommandBuilder(this IServiceCollection services,
+ public static IServiceCollection AddCommand(this IServiceCollection services,
string key)
where TCommandBuilder :
ICommandBuilder, new()
{
TCommandBuilder builder = new();
IHost? host = new HostBuilder()
- .ConfigureServices(services =>
+ .ConfigureServices(isolatedServices =>
{
- services.AddTransient();
- services.AddTransient();
+ isolatedServices.AddTransient();
+ isolatedServices.AddTransient();
- builder.Create(services);
+ builder.Create(isolatedServices);
}).Build();
services.AddTransient(provider => new CommandContext(host.Services));
diff --git a/Hyperbar.Desktop/Views/CommandView.xaml b/Hyperbar.Desktop/Views/CommandView.xaml
index 51581ad..64d9024 100644
--- a/Hyperbar.Desktop/Views/CommandView.xaml
+++ b/Hyperbar.Desktop/Views/CommandView.xaml
@@ -9,6 +9,5 @@
-
diff --git a/Hyperbar.Desktop/Views/CommandViewModel.cs b/Hyperbar.Desktop/Views/CommandViewModel.cs
index b0125e6..66b3220 100644
--- a/Hyperbar.Desktop/Views/CommandViewModel.cs
+++ b/Hyperbar.Desktop/Views/CommandViewModel.cs
@@ -1,6 +1,5 @@
using Hyperbar.Lifecycles;
using Hyperbar.Templates;
-using System.Collections;
using System.Collections.Generic;
namespace Hyperbar.Desktop;
@@ -10,14 +9,13 @@ public partial class CommandViewModel :
ITemplatedViewModel
{
public CommandViewModel(ITemplateFactory templateFactory,
- IEnumerable commands)
+ IEnumerable commands,
+ IWritableConfiguration options)
{
TemplateFactory = templateFactory;
+ AddRange(commands);
- foreach (var command in commands)
- {
- this.Add(command);
- }
+ options.Update(args => { });
}
public ITemplateFactory TemplateFactory { get; }
diff --git a/Hyperbar.sln b/Hyperbar.sln
index 5240bbc..d3664bc 100644
--- a/Hyperbar.sln
+++ b/Hyperbar.sln
@@ -11,9 +11,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hyperbar.Desktop.Win32", "H
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hyperbar", "Hyperbar\Hyperbar.csproj", "{E5795878-C7E3-4386-86FA-33681BCF8D5B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hyperbar.Desktop.Contextual", "Hyperbar.Desktop.Contextual\Hyperbar.Desktop.Contextual.csproj", "{C32D4073-2A9B-4257-8895-09951FAD8E7A}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hyperbar.Desktop.Contextual", "Hyperbar.Desktop.Contextual\Hyperbar.Desktop.Contextual.csproj", "{C32D4073-2A9B-4257-8895-09951FAD8E7A}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hyperbar.Desktop.Primary", "Hyperbar.Desktop.Primary\Hyperbar.Desktop.Primary.csproj", "{AFB8A3EB-8831-4041-AE05-3E0EF672887C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hyperbar.Desktop.Primary", "Hyperbar.Desktop.Primary\Hyperbar.Desktop.Primary.csproj", "{AFB8A3EB-8831-4041-AE05-3E0EF672887C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/Hyperbar/Configuration/ConfigurationWriter.cs b/Hyperbar/Configuration/ConfigurationWriter.cs
new file mode 100644
index 0000000..76a150a
--- /dev/null
+++ b/Hyperbar/Configuration/ConfigurationWriter.cs
@@ -0,0 +1,101 @@
+using System.Text.Encodings.Web;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Hyperbar.Options;
+
+public class ConfigurationWriter(string path,
+ string section,
+ JsonSerializerOptions? serializerOptions = null) :
+ IConfigurationWriter
+ where TConfiguration :
+ class, new()
+{
+ internal static Func DefaultSerializerOptions = new(() =>
+ {
+ return new JsonSerializerOptions
+ {
+ WriteIndented = true,
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ Converters = { new JsonStringEnumConverter() }
+ };
+ });
+
+ private readonly JsonSerializerOptions? serializerOptions = serializerOptions ??= DefaultSerializerOptions();
+
+ public void Write(Action? updateDelegate = null)
+ {
+ TConfiguration? updatedValue = TryGet(out TConfiguration? value) ? value : new TConfiguration();
+
+ updateDelegate?.Invoke(updatedValue);
+ Write(updatedValue);
+ }
+
+ public void Write(TConfiguration? value)
+ {
+ if (!File.Exists(path))
+ {
+ string? fileDirectoryPath = Path.GetDirectoryName(path);
+ if (!string.IsNullOrEmpty(fileDirectoryPath))
+ {
+ Directory.CreateDirectory(fileDirectoryPath);
+ }
+
+ File.WriteAllText(path, "{}");
+ }
+
+ byte[] jsonContent = File.ReadAllBytes(path);
+
+ using JsonDocument jsonDocument = JsonDocument.Parse(jsonContent);
+ using FileStream stream = File.OpenWrite(path);
+ Utf8JsonWriter writer = new(stream, new JsonWriterOptions()
+ {
+ Indented = true,
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
+ });
+
+ writer.WriteStartObject();
+ bool isWritten = false;
+ JsonDocument optionsElement = JsonDocument.Parse(JsonSerializer.SerializeToUtf8Bytes(value, serializerOptions));
+
+ foreach (JsonProperty element in jsonDocument.RootElement.EnumerateObject())
+ {
+ if (element.Name != section)
+ {
+ element.WriteTo(writer);
+ continue;
+ }
+ writer.WritePropertyName(element.Name);
+ optionsElement.WriteTo(writer);
+ isWritten = true;
+ }
+
+ if (!isWritten)
+ {
+ writer.WritePropertyName(section);
+ optionsElement.WriteTo(writer);
+ }
+
+ writer.WriteEndObject();
+ writer.Flush();
+ stream.SetLength(stream.Position);
+ }
+
+ private bool TryGet(out T? value)
+ {
+ if (File.Exists(path))
+ {
+ byte[] jsonContent = File.ReadAllBytes(path);
+
+ using JsonDocument jsonDocument = JsonDocument.Parse(jsonContent);
+ if (jsonDocument.RootElement.TryGetProperty(section, out JsonElement sectionValue))
+ {
+ value = JsonSerializer.Deserialize(sectionValue.ToString(), serializerOptions);
+ return true;
+ }
+ }
+
+ value = default;
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/Hyperbar/Configuration/IConfigurationWriter.cs b/Hyperbar/Configuration/IConfigurationWriter.cs
new file mode 100644
index 0000000..7426659
--- /dev/null
+++ b/Hyperbar/Configuration/IConfigurationWriter.cs
@@ -0,0 +1,12 @@
+
+namespace Hyperbar.Options
+{
+ public interface IConfigurationWriter
+ where TConfiguration :
+ class, new()
+ {
+ void Write(Action? updateDelegate = null);
+
+ void Write(TConfiguration? value);
+ }
+}
\ No newline at end of file
diff --git a/Hyperbar/Configuration/IWritableConfiguration.cs b/Hyperbar/Configuration/IWritableConfiguration.cs
new file mode 100644
index 0000000..f0e74f1
--- /dev/null
+++ b/Hyperbar/Configuration/IWritableConfiguration.cs
@@ -0,0 +1,12 @@
+using Microsoft.Extensions.Options;
+
+namespace Hyperbar;
+
+public interface IWritableConfiguration :
+ IOptionsSnapshot
+ where TConfiguration :
+ class, new()
+{
+ void Update(Action updateAction,
+ bool reload = true);
+}
diff --git a/Hyperbar/Configuration/WritableConfiguration.cs b/Hyperbar/Configuration/WritableConfiguration.cs
new file mode 100644
index 0000000..8f84103
--- /dev/null
+++ b/Hyperbar/Configuration/WritableConfiguration.cs
@@ -0,0 +1,27 @@
+using Hyperbar.Options;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Options;
+
+namespace Hyperbar;
+
+public class WritableConfiguration(IConfigurationWriter writer,
+ IOptionsMonitor options,
+ IConfiguration configuration) :
+ IWritableConfiguration
+ where TConfiguration :
+ class, new()
+{
+ public TConfiguration Value => options.CurrentValue;
+
+ public TConfiguration Get(string? name) => options.Get(name);
+
+ public void Update(Action updateDelegate,
+ bool reload = true)
+ {
+ writer.Write(updateDelegate);
+ if (reload && configuration is IConfigurationRoot configurationRoot)
+ {
+ configurationRoot.Reload();
+ }
+ }
+}
diff --git a/Hyperbar/Configurations/ConfigurationWriter.cs b/Hyperbar/Configurations/ConfigurationWriter.cs
deleted file mode 100644
index 8d42110..0000000
--- a/Hyperbar/Configurations/ConfigurationWriter.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-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
deleted file mode 100644
index b2f414b..0000000
--- a/Hyperbar/Configurations/IConfigurationWriter.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-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
deleted file mode 100644
index f7a7cdb..0000000
--- a/Hyperbar/Configurations/IWritableConfigurationProvider.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-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
deleted file mode 100644
index 2dd777c..0000000
--- a/Hyperbar/Configurations/IWritableJsonConfigurationBuilder.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-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
deleted file mode 100644
index 8e66414..0000000
--- a/Hyperbar/Configurations/IWritableJsonConfigurationDescriptor.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Hyperbar.Configurations;
-
-public interface IWritableJsonConfigurationDescriptor
-{
- Type ConfigurationType { get; }
-
- string Key { get; }
-}
diff --git a/Hyperbar/Configurations/WritableJsonConfigurationBuilder.cs b/Hyperbar/Configurations/WritableJsonConfigurationBuilder.cs
deleted file mode 100644
index 8c84ce6..0000000
--- a/Hyperbar/Configurations/WritableJsonConfigurationBuilder.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-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
deleted file mode 100644
index 8b274c2..0000000
--- a/Hyperbar/Configurations/WritableJsonConfigurationDescriptor.cs
+++ /dev/null
@@ -1,3 +0,0 @@
-namespace Hyperbar.Configurations;
-
-public record WritableJsonConfigurationDescriptor(Type ConfigurationType, string Key) : IWritableJsonConfigurationDescriptor;
diff --git a/Hyperbar/Configurations/WritableJsonConfigurationFile.cs b/Hyperbar/Configurations/WritableJsonConfigurationFile.cs
deleted file mode 100644
index 1c14ced..0000000
--- a/Hyperbar/Configurations/WritableJsonConfigurationFile.cs
+++ /dev/null
@@ -1,147 +0,0 @@
-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
deleted file mode 100644
index 6edfb0f..0000000
--- a/Hyperbar/Configurations/WritableJsonConfigurationProvider.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-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
deleted file mode 100644
index a12f105..0000000
--- a/Hyperbar/Configurations/WritableJsonConfigurationSource.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-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/Extensions/IServiceCollectionExtensions.cs b/Hyperbar/Extensions/IServiceCollectionExtensions.cs
index 0b888c1..66bf49b 100644
--- a/Hyperbar/Extensions/IServiceCollectionExtensions.cs
+++ b/Hyperbar/Extensions/IServiceCollectionExtensions.cs
@@ -1,10 +1,62 @@
using Hyperbar.Lifecycles;
+using Hyperbar.Options;
using Hyperbar.Templates;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.FileProviders;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Options;
+using System.Text.Json;
namespace Hyperbar;
public static class IServiceCollectionExtensions
{
+ public static IServiceCollection ConfigureWritableOptions(this IServiceCollection services,
+ string path = "Settings.json",
+ Func? defaultSerializerOptions = null)
+ where TConfiguration :
+ class, new()
+ {
+ return services.ConfigureWritableOptions(typeof(TConfiguration).Name, path);
+ }
+
+ public static IServiceCollection ConfigureWritableOptions(this IServiceCollection services,
+ string section,
+ string path = "Settings.json",
+ Action? serializerDelegate = null)
+ where TConfiguration :
+ class, new()
+ {
+ services.AddOptions();
+ services.AddSingleton>(new ConfigureNamedOptions("", args => { }));
+
+ services.AddTransient>(provider =>
+ {
+ string? jsonFilePath = null;
+ if (provider.GetService() is IHostEnvironment hostEnvironment)
+ {
+ IFileProvider fileProvider = hostEnvironment.ContentRootFileProvider;
+ IFileInfo fileInfo = fileProvider.GetFileInfo(path);
+
+ jsonFilePath = fileInfo.PhysicalPath;
+ }
+
+ jsonFilePath ??= Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path);
+
+ JsonSerializerOptions? defaultSerializerOptions = null;
+ if (serializerDelegate is not null)
+ {
+ defaultSerializerOptions = new JsonSerializerOptions();
+ serializerDelegate.Invoke(defaultSerializerOptions);
+ }
+
+ return new ConfigurationWriter(jsonFilePath, section, defaultSerializerOptions);
+ });
+
+ services.AddTransient, WritableConfiguration>();
+ return services;
+ }
+
public static IServiceCollection AddCommandTemplate(this IServiceCollection services)
where TCommand :
ICommandViewModel
@@ -13,14 +65,13 @@ public static class IServiceCollectionExtensions
Type templateType = typeof(TCommandTemplate);
string key = dataType.Name;
-
- _ = services.AddTransient(typeof(ICommandViewModel), dataType);
- _ = services.AddTransient(templateType);
-
- _ = services.AddKeyedTransient(typeof(ICommandViewModel), key, dataType);
- _ = services.AddKeyedTransient(templateType, key);
-
- _ = services.AddTransient(provider => new DataTemplateDescriptor
+
+ services.AddTransient(typeof(ICommandViewModel), dataType);
+ services.AddTransient(templateType);
+ services.AddKeyedTransient(typeof(ICommandViewModel), key, dataType);
+ services.AddKeyedTransient(templateType, key);
+
+ services.AddTransient(provider => new DataTemplateDescriptor
{
DataType = dataType,
TemplateType = templateType,
@@ -38,10 +89,10 @@ public static class IServiceCollectionExtensions
key ??= dataType.Name;
- _ = services.AddKeyedTransient(dataType, key);
- _ = services.AddKeyedTransient(templateType, key);
-
- _ = services.AddTransient(provider => new DataTemplateDescriptor
+ services.AddKeyedTransient(dataType, key);
+ services.AddKeyedTransient(templateType, key);
+
+ services.AddTransient(provider => new DataTemplateDescriptor
{
DataType = dataType,
TemplateType = templateType,
diff --git a/Hyperbar/Extensions/WritableJsonConfigurationExtensions.cs b/Hyperbar/Extensions/WritableJsonConfigurationExtensions.cs
deleted file mode 100644
index edff984..0000000
--- a/Hyperbar/Extensions/WritableJsonConfigurationExtensions.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-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 d638b86..68ec528 100644
--- a/Hyperbar/Hyperbar.csproj
+++ b/Hyperbar/Hyperbar.csproj
@@ -3,6 +3,7 @@
net8.0
enable
enable
+ true
diff --git a/Hyperbar/Lifecycles/AppService.cs b/Hyperbar/Lifecycles/AppService.cs
index a18247e..614583f 100644
--- a/Hyperbar/Lifecycles/AppService.cs
+++ b/Hyperbar/Lifecycles/AppService.cs
@@ -1,14 +1,7 @@
using Microsoft.Extensions.Hosting;
-using System.Collections.ObjectModel;
namespace Hyperbar.Lifecycles;
-public class ObservableCollectionViewModel :
- ObservableCollection