Amend configuration to support writing to nested sections
This commit is contained in:
@@ -1,13 +1,6 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Toolkit.Foundation;
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public record ComponentConfiguration
|
||||
{
|
||||
public string? Description { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
|
||||
[JsonInclude]
|
||||
internal Guid Id { get; set; } = Guid.NewGuid();
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public class ConfigurationInitializer<TConfiguration>(IPublisher publisher,
|
||||
public class ConfigurationInitializer<TConfiguration>(string section,
|
||||
IConfigurationReader<TConfiguration> reader,
|
||||
IConfigurationWriter<TConfiguration> writer,
|
||||
IConfigurationFactory<TConfiguration> factory) :
|
||||
IConfigurationFactory<TConfiguration> factory,
|
||||
IPublisher publisher) :
|
||||
IConfigurationInitializer<TConfiguration>,
|
||||
IInitializer
|
||||
where TConfiguration :
|
||||
@@ -11,6 +12,7 @@ public class ConfigurationInitializer<TConfiguration>(IPublisher publisher,
|
||||
{
|
||||
public async Task Initialize()
|
||||
{
|
||||
var d = section;
|
||||
if (!reader.TryRead(out TConfiguration? configuration))
|
||||
{
|
||||
if (factory.Create() is object defaultConfiguration)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using System;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Toolkit.Foundation;
|
||||
@@ -35,7 +37,6 @@ public class ConfigurationSource<TConfiguration>(IConfigurationFile<TConfigurati
|
||||
lock (lockingObject)
|
||||
{
|
||||
IFileInfo fileInfo = configurationFile.FileInfo;
|
||||
|
||||
if (!File.Exists(fileInfo.PhysicalPath))
|
||||
{
|
||||
string? fileDirectoryPath = Path.GetDirectoryName(fileInfo.PhysicalPath);
|
||||
@@ -47,53 +48,71 @@ public class ConfigurationSource<TConfiguration>(IConfigurationFile<TConfigurati
|
||||
File.WriteAllText(fileInfo.PhysicalPath!, "{}");
|
||||
}
|
||||
|
||||
static Stream OpenReadWrite(IFileInfo fileInfo)
|
||||
{
|
||||
return fileInfo.PhysicalPath is not null
|
||||
? new FileStream(fileInfo.PhysicalPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite)
|
||||
using Stream stream = fileInfo.PhysicalPath is not null
|
||||
? new FileStream(fileInfo.PhysicalPath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)
|
||||
: fileInfo.CreateReadStream();
|
||||
}
|
||||
|
||||
using Stream stream = OpenReadWrite(fileInfo);
|
||||
using StreamReader? reader = new(stream);
|
||||
|
||||
string? content = reader.ReadToEnd();
|
||||
using JsonDocument jsonDocument = JsonDocument.Parse(content);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
using Stream stream2 = OpenReadWrite(fileInfo);
|
||||
Utf8JsonWriter writer = new(stream2, new JsonWriterOptions()
|
||||
{
|
||||
Indented = true,
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
});
|
||||
|
||||
writer.WriteStartObject();
|
||||
bool isWritten = false;
|
||||
|
||||
JsonDocument optionsElement = JsonDocument.Parse(JsonSerializer.SerializeToUtf8Bytes(value,
|
||||
JsonNode? rootNode = JsonNode.Parse(content);
|
||||
JsonNode? valueNode = JsonNode.Parse(JsonSerializer.SerializeToUtf8Bytes(value,
|
||||
serializerOptions ?? defaultSerializerOptions()));
|
||||
|
||||
foreach (JsonProperty element in jsonDocument.RootElement.EnumerateObject())
|
||||
string[] segments = section.Split(':');
|
||||
JsonNode? currentNode = rootNode;
|
||||
int lastIndex = segments.Length - 1;
|
||||
|
||||
for (int i = 0; i < lastIndex; i++)
|
||||
{
|
||||
if (element.Name != section)
|
||||
if (currentNode is null)
|
||||
{
|
||||
element.WriteTo(writer);
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
writer.WritePropertyName(element.Name);
|
||||
optionsElement.WriteTo(writer);
|
||||
isWritten = true;
|
||||
|
||||
string currentKey = segments[i];
|
||||
if (currentNode[currentKey] is null)
|
||||
{
|
||||
if (int.TryParse(segments[i + 1], out int _))
|
||||
{
|
||||
currentNode[currentKey] = new JsonArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentNode[currentKey] = new JsonObject();
|
||||
}
|
||||
}
|
||||
|
||||
currentNode = currentNode[currentKey];
|
||||
}
|
||||
|
||||
if (!isWritten)
|
||||
if (currentNode is not null)
|
||||
{
|
||||
writer.WritePropertyName(section);
|
||||
optionsElement.WriteTo(writer);
|
||||
string lastKey = segments[lastIndex];
|
||||
if (currentNode is JsonArray array && int.TryParse(lastKey, out int index))
|
||||
{
|
||||
if (array.Count <= index)
|
||||
{
|
||||
array.Add(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
array[index] = valueNode;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
currentNode[lastKey] = valueNode;
|
||||
}
|
||||
}
|
||||
|
||||
writer.WriteEndObject();
|
||||
writer.Flush();
|
||||
stream2.SetLength(stream2.Position);
|
||||
using Stream stream2 = fileInfo.PhysicalPath is not null
|
||||
? new FileStream(fileInfo.PhysicalPath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)
|
||||
: fileInfo.CreateReadStream();
|
||||
|
||||
JsonSerializer.Serialize(stream, rootNode, serializerOptions ?? defaultSerializerOptions());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Toolkit.Foundation;
|
||||
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public interface INavigationViewModel :
|
||||
IObservableViewModel
|
||||
{
|
||||
string Text { get; set; }
|
||||
}
|
||||
@@ -49,6 +49,11 @@ public static class IServiceCollectionExtensions
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddConfiguration<TConfiguration>(this IServiceCollection services,
|
||||
string section)
|
||||
where TConfiguration : class =>
|
||||
services.AddConfiguration<TConfiguration>(section, "Settings.json", null);
|
||||
|
||||
public static IServiceCollection AddConfiguration<TConfiguration>(this IServiceCollection services)
|
||||
where TConfiguration : class =>
|
||||
services.AddConfiguration<TConfiguration>(typeof(TConfiguration).Name, "Settings.json", null);
|
||||
@@ -63,6 +68,17 @@ public static class IServiceCollectionExtensions
|
||||
return services.AddConfiguration(typeof(TConfiguration).Name, "Settings.json", configuration);
|
||||
}
|
||||
|
||||
public static IServiceCollection AddConfiguration<TConfiguration>(this IServiceCollection services,
|
||||
Action<TConfiguration> configurationDelegate,
|
||||
string section)
|
||||
where TConfiguration : class, new()
|
||||
{
|
||||
TConfiguration configuration = new();
|
||||
configurationDelegate.Invoke(configuration);
|
||||
|
||||
return services.AddConfiguration(section, "Settings.json", configuration);
|
||||
}
|
||||
|
||||
public static IServiceCollection AddConfiguration<TConfiguration>(this IServiceCollection services,
|
||||
TConfiguration configuration)
|
||||
where TConfiguration : class =>
|
||||
@@ -81,20 +97,7 @@ public static class IServiceCollectionExtensions
|
||||
Action<JsonSerializerOptions>? serializerDelegate = null)
|
||||
where TConfiguration : class
|
||||
{
|
||||
services.AddSingleton<IConfigurationSource<TConfiguration>>(provider =>
|
||||
{
|
||||
JsonSerializerOptions? defaultSerializer = null;
|
||||
if (serializerDelegate is not null)
|
||||
{
|
||||
defaultSerializer = new JsonSerializerOptions();
|
||||
serializerDelegate.Invoke(defaultSerializer);
|
||||
}
|
||||
|
||||
return new ConfigurationSource<TConfiguration>(provider.GetRequiredService<IConfigurationFile<TConfiguration>>(),
|
||||
section, defaultSerializer);
|
||||
});
|
||||
|
||||
services.AddSingleton<IConfigurationFile<TConfiguration>>(provider =>
|
||||
services.TryAddSingleton<IConfigurationFile<TConfiguration>>(provider =>
|
||||
{
|
||||
IFileInfo? fileInfo = null;
|
||||
if (provider.GetService<IHostEnvironment>() is IHostEnvironment hostEnvironment)
|
||||
@@ -107,15 +110,39 @@ public static class IServiceCollectionExtensions
|
||||
return new ConfigurationFile<TConfiguration>(fileInfo);
|
||||
});
|
||||
|
||||
services.AddHostedService<ConfigurationMonitor<TConfiguration>>();
|
||||
services.AddSingleton<IConfigurationReader<TConfiguration>, ConfigurationReader<TConfiguration>>();
|
||||
services.AddSingleton<IConfigurationWriter<TConfiguration>, ConfigurationWriter<TConfiguration>>();
|
||||
services.TryAddKeyedSingleton<IConfigurationSource<TConfiguration>>(section, (provider, KeyAccelerator) =>
|
||||
{
|
||||
JsonSerializerOptions? defaultSerializer = null;
|
||||
if (serializerDelegate is not null)
|
||||
{
|
||||
defaultSerializer = new JsonSerializerOptions();
|
||||
serializerDelegate.Invoke(defaultSerializer);
|
||||
}
|
||||
|
||||
services.AddTransient<IConfigurationFactory<TConfiguration>>(provider => new ConfigurationFactory<TConfiguration>(() =>
|
||||
configuration ?? provider.GetRequiredService<TConfiguration>()));
|
||||
return new ConfigurationSource<TConfiguration>(provider.GetRequiredService<IConfigurationFile<TConfiguration>>(),
|
||||
section, defaultSerializer);
|
||||
});
|
||||
|
||||
services.AddTransient<IInitializer, ConfigurationInitializer<TConfiguration>>();
|
||||
services.AddTransient<IConfigurationInitializer<TConfiguration>, ConfigurationInitializer<TConfiguration>>();
|
||||
//services.AddHostedService<ConfigurationMonitor<TConfiguration>>();
|
||||
services.TryAddKeyedTransient<IConfigurationReader<TConfiguration>>(section, (provider, key) =>
|
||||
new ConfigurationReader<TConfiguration>(provider.GetRequiredKeyedService<IConfigurationSource<TConfiguration>>(key),
|
||||
provider.GetRequiredKeyedService<IConfigurationFactory<TConfiguration>>(key)));
|
||||
|
||||
services.TryAddKeyedTransient<IConfigurationWriter<TConfiguration>>(section, (provider, key) =>
|
||||
new ConfigurationWriter<TConfiguration>(provider.GetRequiredKeyedService<IConfigurationSource<TConfiguration>>(key)));
|
||||
|
||||
services.TryAddKeyedTransient<IConfigurationFactory<TConfiguration>>(section, (provider, key) =>
|
||||
new ConfigurationFactory<TConfiguration>(() => configuration ?? provider.GetRequiredService<TConfiguration>()));
|
||||
|
||||
services.AddTransient<IInitializer, ConfigurationInitializer<TConfiguration>>(provider =>
|
||||
new ConfigurationInitializer<TConfiguration>(section,
|
||||
provider.GetRequiredKeyedService<IConfigurationReader<TConfiguration>>(section),
|
||||
provider.GetRequiredKeyedService<IConfigurationWriter<TConfiguration>>(section),
|
||||
provider.GetRequiredKeyedService<IConfigurationFactory<TConfiguration>>(section),
|
||||
provider.GetRequiredService<IPublisher>()));
|
||||
|
||||
services.AddTransient<IConfigurationInitializer<TConfiguration>, ConfigurationInitializer<TConfiguration>>(provider =>
|
||||
provider.GetRequiredService<IServiceFactory>().Create<ConfigurationInitializer<TConfiguration>>(section));
|
||||
|
||||
services.AddTransient<IWritableConfiguration<TConfiguration>, WritableConfiguration<TConfiguration>>();
|
||||
|
||||
@@ -125,31 +152,6 @@ public static class IServiceCollectionExtensions
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddTemplate<TViewModel, TView>(this IServiceCollection services,
|
||||
object? key = null,
|
||||
params object[]? parameters)
|
||||
{
|
||||
Type viewModelType = typeof(TViewModel);
|
||||
Type viewType = typeof(TView);
|
||||
|
||||
key ??= viewModelType.Name.Replace("ViewModel", "");
|
||||
|
||||
services.AddTransient(viewModelType, provider =>
|
||||
provider.GetRequiredService<IServiceFactory>().Create<TViewModel>(parameters)!);
|
||||
|
||||
services.AddTransient(viewType);
|
||||
|
||||
services.AddKeyedTransient(viewModelType, key, (provider, key) =>
|
||||
provider.GetRequiredService<IServiceFactory>().Create<TViewModel>(parameters)!);
|
||||
|
||||
services.AddKeyedTransient(viewType, key);
|
||||
|
||||
services.AddTransient<IContentTemplateDescriptor>(provider =>
|
||||
new ContentTemplateDescriptor(key, viewModelType, viewType, parameters));
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddHandler<THandler>(this IServiceCollection services,
|
||||
ServiceLifetime lifetime = ServiceLifetime.Transient)
|
||||
where THandler : IHandler
|
||||
@@ -231,6 +233,7 @@ public static class IServiceCollectionExtensions
|
||||
services.AddHandler<THandler>();
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddRange(this IServiceCollection services,
|
||||
IServiceCollection fromServices)
|
||||
{
|
||||
@@ -241,4 +244,29 @@ public static class IServiceCollectionExtensions
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddTemplate<TViewModel, TView>(this IServiceCollection services,
|
||||
object? key = null,
|
||||
params object[]? parameters)
|
||||
{
|
||||
Type viewModelType = typeof(TViewModel);
|
||||
Type viewType = typeof(TView);
|
||||
|
||||
key ??= viewModelType.Name.Replace("ViewModel", "");
|
||||
|
||||
services.AddTransient(viewModelType, provider =>
|
||||
provider.GetRequiredService<IServiceFactory>().Create<TViewModel>(parameters)!);
|
||||
|
||||
services.AddTransient(viewType);
|
||||
|
||||
services.AddKeyedTransient(viewModelType, key, (provider, key) =>
|
||||
provider.GetRequiredService<IServiceFactory>().Create<TViewModel>(parameters)!);
|
||||
|
||||
services.AddKeyedTransient(viewType, key);
|
||||
|
||||
services.AddTransient<IContentTemplateDescriptor>(provider =>
|
||||
new ContentTemplateDescriptor(key, viewModelType, viewType, parameters));
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public partial class NavigationViewModel(IServiceProvider serviceProvider,
|
||||
IServiceFactory serviceFactory,
|
||||
IPublisher publisher,
|
||||
ISubscriber subscriber,
|
||||
IDisposer disposer,
|
||||
string text) :
|
||||
ObservableViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer),
|
||||
INavigationViewModel
|
||||
{
|
||||
[ObservableProperty]
|
||||
private string? text = text;
|
||||
}
|
||||
|
||||
public partial class NavigationViewModel<TNavigationViewModel>(IServiceProvider serviceProvider,
|
||||
IServiceFactory serviceFactory,
|
||||
IPublisher publisher,
|
||||
ISubscriber subscriber,
|
||||
IDisposer disposer,
|
||||
string text) :
|
||||
ObservableViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer),
|
||||
INavigationViewModel
|
||||
where TNavigationViewModel :
|
||||
INavigationViewModel
|
||||
{
|
||||
[ObservableProperty]
|
||||
private string? text = text;
|
||||
}
|
||||
Reference in New Issue
Block a user