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(); 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 configurationAction = null) { var config = new ConfigurationHelperConfig(); configurationAction?.Invoke(config); services.AddSingleton(UnderscoredNamingConvention.Instance); services.AddSingleton(s => new SerializerBuilder().WithNamingConvention(s.GetRequiredService()).WithTypeConverter(new YamlNullableEnumTypeConverter()).Build()); services.AddSingleton(s => new DeserializerBuilder().WithNamingConvention(s.GetRequiredService()).WithTypeConverter(new YamlNullableEnumTypeConverter()).Build()); services.AddTransient(); services.AddSingleton(new ConfigurationPathProvider(config.ConfigurationDirectory)); } /// /// The file will be named with underscore case with .yaml as its file extension /// /// /// /// public static void AddConfigurationsFromDirectory(this IServiceCollection services, string path) where T : class, new() { services.AddSingleton>(s => s.GetRequiredService() .GetConfigurations(s.GetService().GetConfigurationPath(path))); } /// /// The file will be named with underscore case with .yaml as its file extension /// /// /// /// public static void AddFileConfiguration(this IServiceCollection services, string configName) where T : class, new() { services.AddSingleton(s => s.GetRequiredService().Load( s.GetService().GetConfigurationPath(configName.ToUnderscoreCase() + ".yaml"), true)); } /// /// The file will be named with underscore case with .yaml as its file extension /// /// /// /// public static void AddMultipleConfigurationOneFile(this IServiceCollection services, string configName) where T : class, new() { services.AddSingleton>(s => s.GetRequiredService().Load>( s.GetService().GetConfigurationPath(configName.ToUnderscoreCase() + ".yaml"), true)); } /// /// The file will be named with underscore case with .yaml as its file extension /// /// /// public static void AddFileConfiguration(this IServiceCollection services) where T : class, new() { services.AddSingleton(s => s.GetRequiredService().Load( s.GetService().GetConfigurationPath(typeof(T).Name.ToUnderscoreCase() + ".yaml"), true)); } /// /// The file will be named with underscore case with .yaml as its file extension /// /// /// Default value in case the file does not exist /// public static void AddFileConfiguration(this IServiceCollection services, T defaultConfig) where T : class, new() { services.AddSingleton(s => s.GetRequiredService().Load( s.GetService().GetConfigurationPath(typeof(T).Name.ToUnderscoreCase() + ".yaml"), defaultConfig)); } private static List GetConfigurations(this IConfigurationHelper helper, string path) where T : class, new() { var configs = new List(); 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(file); configs.Add(config); Log.Info($"[CONFIGURATION_HELPER] Loading configuration from {fileInfo.Name} as {typeof(T).Name}"); } return configs; } } }