< Summary

Information
Class: Orchestrator.Infrastructure.TypeResolver
Assembly: Orchestrator
File(s): /home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Infrastructure/TypeResolver.cs
Line coverage
100%
Covered lines: 12
Uncovered lines: 0
Coverable lines: 12
Total lines: 62
Line coverage: 100%
Branch coverage
100%
Covered branches: 10
Total branches: 10
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%44100%
Resolve(...)100%22100%
Dispose()100%44100%

File(s)

/home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Infrastructure/TypeResolver.cs

#LineLine coverage
 1using Microsoft.Extensions.Hosting;
 2using Spectre.Console.Cli;
 3
 4namespace Orchestrator.Infrastructure;
 5
 6/// <summary>
 7/// Resolves types from the DI container for Spectre.Console.Cli.
 8/// </summary>
 9/// <remarks>
 10/// <para>
 11/// Based on the canonical implementation from spectreconsole/examples:
 12/// <see href="https://github.com/spectreconsole/examples/blob/main/examples/Cli/Logging/Infrastructure/TypeResolver.cs"
 13/// </para>
 14/// <para>
 15/// Extended to stop <see cref="IHostedService"/> instances on disposal, ensuring
 16/// graceful shutdown of services started by <see cref="TypeRegistrar.Build"/>.
 17/// </para>
 18/// </remarks>
 19public sealed class TypeResolver : ITypeResolver, IDisposable
 20{
 21    private readonly IServiceProvider _provider;
 22    private readonly IReadOnlyList<IHostedService> _hostedServices;
 23
 124    public TypeResolver(IServiceProvider provider, IReadOnlyList<IHostedService> hostedServices)
 25    {
 126        _provider = provider ?? throw new ArgumentNullException(nameof(provider));
 127        _hostedServices = hostedServices ?? throw new ArgumentNullException(nameof(hostedServices));
 128    }
 29
 30    public object? Resolve(Type? type)
 31    {
 132        if (type is null)
 33        {
 134            return null;
 35        }
 36
 137        return _provider.GetService(type);
 38    }
 39
 40    public void Dispose()
 41    {
 42        // Stop hosted services before disposing the provider to allow graceful shutdown
 43        // (e.g. OpenTelemetry TracerProvider flush).
 44        //
 45        // Blocking wait rationale: Spectre.Console.Cli disposes our resolver through
 46        // TypeResolverAdapter.Dispose(), which only checks for IDisposable — not IAsyncDisposable.
 47        // The enclosing `using` block in CommandExecutor.ExecuteAsync is synchronous, so there is no
 48        // async disposal path we can participate in. IHostedService only exposes async lifecycle
 49        // methods, so the .GetAwaiter().GetResult() bridge is required here.
 50        // See: spectre.console.cli/src/Spectre.Console.Cli/Internal/TypeResolverAdapter.cs
 51        //      spectre.console.cli/src/Spectre.Console.Cli/Internal/CommandExecutor.cs (~line 88)
 152        foreach (var service in _hostedServices)
 53        {
 154            service.StopAsync(CancellationToken.None).GetAwaiter().GetResult();
 55        }
 56
 157        if (_provider is IDisposable disposable)
 58        {
 159            disposable.Dispose();
 60        }
 161    }
 62}