server-master/srcs/PhoenixLib.Configuration/DependencyInjectionExtensions.cs
2026-02-10 18:21:30 +01:00

164 lines
No EOL
6.8 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using PhoenixLib.Logging;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace PhoenixLib.Configuration
{
public class YamlNullableEnumTypeConverter : IYamlTypeConverter
{
public bool Accepts(Type type) => Nullable.GetUnderlyingType(type)?.IsEnum ?? false;
public object ReadYaml(IParser parser, Type type)
{
type = Nullable.GetUnderlyingType(type) ?? throw new ArgumentException("Expected nullable enum type for ReadYaml");
Scalar scalar = parser.Consume<Scalar>();
if (string.IsNullOrWhiteSpace(scalar.Value))
{
return null;
}
try
{
return Enum.Parse(type, scalar.Value);
}
catch (Exception ex)
{
throw new Exception($"Invalid value: \"{scalar.Value}\" for {type.Name}", ex);
}
}
public void WriteYaml(IEmitter emitter, object value, Type type)
{
type = Nullable.GetUnderlyingType(type) ?? throw new ArgumentException("Expected nullable enum type for WriteYaml");
if (value == null)
{
return;
}
string? toWrite = Enum.GetName(type, value) ?? throw new InvalidOperationException($"Invalid value {value} for enum: {type}");
emitter.Emit(new Scalar(null, null, toWrite, ScalarStyle.Any, true, false));
}
}
public static class DependencyInjectionExtensions
{
public static void AddYamlConfigurationHelper(this IServiceCollection services,
Action<ConfigurationHelperConfig> configurationAction = null)
{
var config = new ConfigurationHelperConfig();
configurationAction?.Invoke(config);
services.AddSingleton(UnderscoredNamingConvention.Instance);
services.AddSingleton(s =>
new SerializerBuilder().WithNamingConvention(s.GetRequiredService<INamingConvention>()).WithTypeConverter(new YamlNullableEnumTypeConverter()).Build());
services.AddSingleton(s =>
new DeserializerBuilder().WithNamingConvention(s.GetRequiredService<INamingConvention>()).WithTypeConverter(new YamlNullableEnumTypeConverter()).Build());
services.AddTransient<IConfigurationHelper, YamlConfigurationHelper>();
services.AddSingleton<IConfigurationPathProvider>(new ConfigurationPathProvider(config.ConfigurationDirectory));
}
/// <summary>
/// The file will be named with underscore case with .yaml as its file extension
/// </summary>
/// <param name="services"></param>
/// <param name="path"></param>
/// <typeparam name="T"></typeparam>
public static void AddConfigurationsFromDirectory<T>(this IServiceCollection services, string path)
where T : class, new()
{
services.AddSingleton<IEnumerable<T>>(s =>
s.GetRequiredService<IConfigurationHelper>()
.GetConfigurations<T>(s.GetService<IConfigurationPathProvider>().GetConfigurationPath(path)));
}
/// <summary>
/// The file will be named with underscore case with .yaml as its file extension
/// </summary>
/// <param name="services"></param>
/// <param name="configName"></param>
/// <typeparam name="T"></typeparam>
public static void AddFileConfiguration<T>(this IServiceCollection services, string configName)
where T : class, new()
{
services.AddSingleton(s =>
s.GetRequiredService<IConfigurationHelper>().Load<T>(
s.GetService<IConfigurationPathProvider>().GetConfigurationPath(configName.ToUnderscoreCase() + ".yaml"), true));
}
/// <summary>
/// The file will be named with underscore case with .yaml as its file extension
/// </summary>
/// <param name="services"></param>
/// <param name="configName"></param>
/// <typeparam name="T"></typeparam>
public static void AddMultipleConfigurationOneFile<T>(this IServiceCollection services, string configName)
where T : class, new()
{
services.AddSingleton<IEnumerable<T>>(s =>
s.GetRequiredService<IConfigurationHelper>().Load<List<T>>(
s.GetService<IConfigurationPathProvider>().GetConfigurationPath(configName.ToUnderscoreCase() + ".yaml"), true));
}
/// <summary>
/// The file will be named with underscore case with .yaml as its file extension
/// </summary>
/// <param name="services"></param>
/// <typeparam name="T"></typeparam>
public static void AddFileConfiguration<T>(this IServiceCollection services) where T : class, new()
{
services.AddSingleton(s =>
s.GetRequiredService<IConfigurationHelper>().Load<T>(
s.GetService<IConfigurationPathProvider>().GetConfigurationPath(typeof(T).Name.ToUnderscoreCase() + ".yaml"),
true));
}
/// <summary>
/// The file will be named with underscore case with .yaml as its file extension
/// </summary>
/// <param name="services"></param>
/// <param name="defaultConfig">Default value in case the file does not exist</param>
/// <typeparam name="T"></typeparam>
public static void AddFileConfiguration<T>(this IServiceCollection services, T defaultConfig)
where T : class, new()
{
services.AddSingleton(s =>
s.GetRequiredService<IConfigurationHelper>().Load(
s.GetService<IConfigurationPathProvider>().GetConfigurationPath(typeof(T).Name.ToUnderscoreCase() + ".yaml"),
defaultConfig));
}
private static List<T> GetConfigurations<T>(this IConfigurationHelper helper, string path)
where T : class, new()
{
var configs = new List<T>();
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
return configs;
}
foreach (string file in Directory.GetFiles(path, "*.yaml", SearchOption.AllDirectories).Concat(Directory.GetFiles(path, "*.yml", SearchOption.AllDirectories)))
{
var fileInfo = new FileInfo(file);
T config = helper.Load<T>(file);
configs.Add(config);
Log.Info($"[CONFIGURATION_HELPER] Loading configuration from {fileInfo.Name} as {typeof(T).Name}");
}
return configs;
}
}
}