using System.Text.RegularExpressions; using Tomlyn; namespace Luna.Configuration.Services.Providers; internal sealed class ProviderOptionsProvider(string? searchPath = null) { private readonly EmbeddedResourceLoader embeddedResourceLoader = new(); public IEnumerable<(string Name, ProviderOptions Options)> GetProviderOptions() { return string.IsNullOrWhiteSpace(searchPath) ? GetProviderOptionsFromEmbeddedResources() : GetProviderOptionsFromFiles(); } private IEnumerable<(string Name, ProviderOptions Options)> GetProviderOptionsFromFiles() { ArgumentException.ThrowIfNullOrEmpty(searchPath); var files = Directory.Exists(searchPath) ? Directory.EnumerateFiles(searchPath, "Provider.*.toml") : []; foreach (var file in files) { var name = Path.GetFileName(file).ToProviderName(); using var reader = File.OpenText(file); var providerOptions = TomlSerializer.Deserialize(reader); yield return (name, providerOptions ?? throw new FormatException($"Invalid provider config file: {file}")); } } private IEnumerable<(string Name, ProviderOptions Options)> GetProviderOptionsFromEmbeddedResources() { var resources = embeddedResourceLoader.ListResources("Provider.*.toml"); foreach (var resource in resources) { var name = resource.ToProviderName(); var content = embeddedResourceLoader.LoadAsync(resource).GetAwaiter().GetResult(); var providerOptions = TomlSerializer.Deserialize(content); yield return (name, providerOptions ?? throw new FormatException($"Invalid provider config embedded resource: {resource}")); } } } internal static partial class ProviderStringExtensions { [GeneratedRegex(@"Provider\.(.+)\.toml", RegexOptions.IgnoreCase | RegexOptions.Compiled)] private static partial Regex ProviderNameRegex { get; } extension(string str) { public string ToProviderName() => ProviderNameRegex.Match(str) is { Success: true } match ? match.Groups[1].Value : throw new FormatException($"Invalid provider config file name: {str}"); } }