namespace Zcbot.WindowsNode; internal sealed class AdapterCatalog { private static readonly Lazy> InstalledDescriptors = new(DiscoverDescriptors, LazyThreadSafetyMode.ExecutionAndPublication); private static readonly JobExecutionGate SharedExecutionGate = new(); private static readonly ProcessSupervisor SharedProcessSupervisor = new(); private readonly IReadOnlyDictionary adapters; internal AdapterCatalog(IEnumerable values) { adapters = values.ToDictionary(item => item.Capability, StringComparer.Ordinal); } internal static string AdapterRoot => Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "adapters")); internal static AdapterCatalog CreateDefault( JobRepository inbox, NodePaths? paths = null) { var resolvedPaths = paths ?? NodePaths.ForCurrentMachine(); return new(InstalledDescriptors.Value.Select(item => new ProcessNodeAdapter( item, inbox, resolvedPaths, SharedExecutionGate, SharedProcessSupervisor))); } internal static IReadOnlyList InstalledContracts => InstalledDescriptors.Value.Select(item => item.Contract).ToArray(); internal static IReadOnlyList InstalledCapabilities => InstalledContracts.Select(item => item.Capability).ToArray(); internal static bool HasActiveExecution => SharedExecutionGate.IsActive; internal IReadOnlyCollection All => adapters.Values.ToArray(); internal INodeAdapter? Find(string capability) => adapters.TryGetValue(capability, out var adapter) ? adapter : null; internal void InvalidateRuntime(string runtimeId) { foreach (var adapter in adapters.Values.Where( item => runtimeId.Equals(item.RuntimeId, StringComparison.Ordinal))) { adapter.InvalidateRuntime(); } } private static IReadOnlyList DiscoverDescriptors() { if (!Directory.Exists(AdapterRoot)) return []; return Directory.EnumerateDirectories(AdapterRoot) .Where(path => File.Exists(Path.Combine(path, "adapter.json"))) .OrderBy(path => path, StringComparer.OrdinalIgnoreCase) .Select(AdapterContractLoader.Load) .ToArray(); } }