tools/code/extractor/Configuration.cs (73 lines of code) (raw):

using common; using LanguageExt; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using System; using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; namespace extractor; public delegate Option<FrozenSet<TName>> FindConfigurationNames<TName>() where TName : ResourceName; public sealed class FindConfigurationNamesFactory(ConfigurationJson configurationJson) { private static readonly FrozenDictionary<Type, string> typeSectionNames = GetTypeSectionNames(); private static readonly FrozenDictionary<string, Type> sectionNameTypes = GetSectionNameTypes(typeSectionNames); private readonly FrozenDictionary<Type, FrozenSet<string>> namesToExtract = GetNamesToExtract(configurationJson); private static FrozenDictionary<Type, string> GetTypeSectionNames() => new Dictionary<Type, string> { [typeof(NamedValueName)] = "namedValueNames", [typeof(TagName)] = "tagNames", [typeof(GatewayName)] = "gatewayNames", [typeof(VersionSetName)] = "versionSetNames", [typeof(BackendName)] = "backendNames", [typeof(LoggerName)] = "loggerNames", [typeof(DiagnosticName)] = "diagnosticNames", [typeof(PolicyFragmentName)] = "policyFragmentNames", [typeof(ProductName)] = "productNames", [typeof(GroupName)] = "groupNames", [typeof(SubscriptionName)] = "subscriptionNames", [typeof(ApiName)] = "apiNames", [typeof(WorkspaceName)] = "workspaceNames", } .ToFrozenDictionary(); private static FrozenDictionary<string, Type> GetSectionNameTypes(FrozenDictionary<Type, string> typeSectionNames) => typeSectionNames.ToFrozenDictionary(kvp => kvp.Value, kvp => kvp.Key, StringComparer.OrdinalIgnoreCase); private static FrozenDictionary<Type, FrozenSet<string>> GetNamesToExtract(ConfigurationJson configurationJson) => configurationJson.Value // Get configuration sections that are JSON arrays .ChooseValues(node => node.TryAsJsonArray().ToOption()) // Map each JSON array to a set of strings .Select(kvp => kvp.MapValue(jsonArray => jsonArray.PickStrings() .Where(value => string.IsNullOrWhiteSpace(value) is false) .ToFrozenSet(StringComparer.OrdinalIgnoreCase))) // Map each configuration section to a resource name type .ChooseKeys(sectionNameTypes.Find) .ToFrozenDictionary(); public static string GetConfigurationSectionName<T>() => typeSectionNames.Find(typeof(T)) .IfNone(() => throw new InvalidOperationException($"Resource type {typeof(T).Name} is not supported.")); public static string GetNameToFind<T>(T name) where T : ResourceName => typeSectionNames.ContainsKey(typeof(T)) ? name switch { ApiName apiName => ApiName.GetRootName(apiName).Value, _ => name.Value } : throw new InvalidOperationException($"Resource type {typeof(T).Name} is not supported."); public FindConfigurationNames<TName> Create<TName>() where TName : ResourceName, IResourceName<TName> => () => namesToExtract.Find(typeof(TName)) .Map(set => set.Select(TName.From) .ToFrozenSet()); } internal static class ConfigurationModule { public static void ConfigureFindConfigurationNamesFactory(IHostApplicationBuilder builder) { common.ConfigurationModule.ConfigureConfigurationJson(builder); builder.Services.TryAddSingleton(GetFindConfigurationNamesFactory); } private static FindConfigurationNamesFactory GetFindConfigurationNamesFactory(IServiceProvider provider) { var configurationJson = provider.GetRequiredService<ConfigurationJson>(); return new FindConfigurationNamesFactory(configurationJson); } }