< Summary

Information
Class: Orchestrator.Infrastructure.TypeRegistrar
Assembly: Orchestrator
File(s): /home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Infrastructure/TypeRegistrar.cs
Line coverage
100%
Covered lines: 15
Uncovered lines: 0
Coverable lines: 15
Total lines: 69
Line coverage: 100%
Branch coverage
100%
Covered branches: 2
Total branches: 2
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%11100%
Build()100%22100%
Register(...)100%11100%
RegisterInstance(...)100%11100%
RegisterLazy(...)100%11100%

File(s)

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

#LineLine coverage
 1using Microsoft.Extensions.DependencyInjection;
 2using Microsoft.Extensions.Hosting;
 3using Spectre.Console.Cli;
 4
 5namespace Orchestrator.Infrastructure;
 6
 7/// <summary>
 8/// Bridges Microsoft.Extensions.DependencyInjection with Spectre.Console.Cli's <see cref="ITypeRegistrar"/>.
 9/// </summary>
 10/// <remarks>
 11/// <para>
 12/// Based on the canonical implementation from spectreconsole/examples:
 13/// <see href="https://github.com/spectreconsole/examples/blob/main/examples/Cli/Logging/Infrastructure/TypeRegistrar.cs
 14/// </para>
 15/// <para>
 16/// Extended to start <see cref="IHostedService"/> instances after building the
 17/// <see cref="ServiceProvider"/>. This is necessary because Spectre.Console.Cli does not use
 18/// <c>IHost</c>, so hosted services (e.g. OpenTelemetry's <c>TelemetryHostedService</c>)
 19/// would never be started otherwise.
 20/// </para>
 21/// </remarks>
 22public sealed class TypeRegistrar : ITypeRegistrar
 23{
 24    private readonly IServiceCollection _builder;
 25
 126    public TypeRegistrar(IServiceCollection builder)
 27    {
 128        _builder = builder;
 129    }
 30
 31    public ITypeResolver Build()
 32    {
 133        var provider = _builder.BuildServiceProvider();
 34
 35        // Start any registered IHostedService instances so they can initialize
 36        // (e.g. OpenTelemetry builds its TracerProvider during StartAsync).
 37        //
 38        // Blocking wait rationale: IHostedService only exposes async lifecycle methods, but
 39        // Spectre.Console.Cli calls Build() from a synchronous context (CommandExecutor.ExecuteAsync
 40        // creates a synchronous `using` block around TypeResolverAdapter, which wraps our resolver).
 41        // TypeResolverAdapter.Dispose() only checks for IDisposable — not IAsyncDisposable — so
 42        // there is no async disposal path available. The .GetAwaiter().GetResult() bridge is required.
 43        // See: spectre.console.cli/src/Spectre.Console.Cli/Internal/TypeResolverAdapter.cs
 44        //      spectre.console.cli/src/Spectre.Console.Cli/Internal/CommandExecutor.cs (~line 88)
 145        var hostedServices = provider.GetServices<IHostedService>().ToList();
 146        foreach (var service in hostedServices)
 47        {
 148            service.StartAsync(CancellationToken.None).GetAwaiter().GetResult();
 49        }
 50
 151        return new TypeResolver(provider, hostedServices);
 52    }
 53
 54    public void Register(Type service, Type implementation)
 55    {
 156        _builder.AddSingleton(service, implementation);
 157    }
 58
 59    public void RegisterInstance(Type service, object implementation)
 60    {
 161        _builder.AddSingleton(service, implementation);
 162    }
 63
 64    public void RegisterLazy(Type service, Func<object> func)
 65    {
 166        ArgumentNullException.ThrowIfNull(func);
 167        _builder.AddSingleton(service, _ => func());
 168    }
 69}