using System.Collections.Concurrent; namespace StellaOps.Scheduler.Plugin; /// /// Thread-safe in-memory registry for scheduler job plugins. /// public sealed class SchedulerPluginRegistry : ISchedulerPluginRegistry { private readonly ConcurrentDictionary _plugins = new(StringComparer.OrdinalIgnoreCase); /// public void Register(ISchedulerJobPlugin plugin) { ArgumentNullException.ThrowIfNull(plugin); if (string.IsNullOrWhiteSpace(plugin.JobKind)) { throw new ArgumentException("Plugin JobKind must not be null or whitespace.", nameof(plugin)); } if (!_plugins.TryAdd(plugin.JobKind, plugin)) { throw new InvalidOperationException( $"A plugin with JobKind '{plugin.JobKind}' is already registered. " + $"Existing: '{_plugins[plugin.JobKind].DisplayName}', " + $"Attempted: '{plugin.DisplayName}'."); } } /// public ISchedulerJobPlugin? Resolve(string jobKind) { if (string.IsNullOrWhiteSpace(jobKind)) { return null; } return _plugins.TryGetValue(jobKind, out var plugin) ? plugin : null; } /// public IReadOnlyList<(string JobKind, string DisplayName)> ListRegistered() { return _plugins.Values .OrderBy(p => p.JobKind, StringComparer.OrdinalIgnoreCase) .Select(p => (p.JobKind, p.DisplayName)) .ToArray(); } }