using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; namespace StellaOps.Scanner.Worker.Processing; public sealed class ScanJobProcessor { private readonly IReadOnlyDictionary _executors; private readonly ScanProgressReporter _progressReporter; private readonly ILogger _logger; public ScanJobProcessor(IEnumerable executors, ScanProgressReporter progressReporter, ILogger logger) { _progressReporter = progressReporter ?? throw new ArgumentNullException(nameof(progressReporter)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); var map = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var executor in executors ?? Array.Empty()) { if (executor is null || string.IsNullOrWhiteSpace(executor.StageName)) { continue; } map[executor.StageName] = executor; } foreach (var stage in ScanStageNames.Ordered) { if (map.ContainsKey(stage)) { continue; } map[stage] = new NoOpStageExecutor(stage); _logger.LogDebug("No executor registered for stage {Stage}; using no-op placeholder.", stage); } _executors = map; } public async ValueTask ExecuteAsync(ScanJobContext context, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(context); foreach (var stage in ScanStageNames.Ordered) { cancellationToken.ThrowIfCancellationRequested(); if (!_executors.TryGetValue(stage, out var executor)) { continue; } await _progressReporter.ExecuteStageAsync( context, stage, executor.ExecuteAsync, cancellationToken).ConfigureAwait(false); } } }