< Summary

Information
Class: DotNetApiDiff.Commands.TypeRegistrar
Assembly: DotNetApiDiff
File(s): /home/runner/work/dotnet-api-diff/dotnet-api-diff/src/DotNetApiDiff/Commands/TypeRegistrar.cs
Line coverage
88%
Covered lines: 38
Uncovered lines: 5
Coverable lines: 43
Total lines: 180
Line coverage: 88.3%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
.ctor(...)100%1181.48%
Build()100%11100%
Register(...)100%11100%
RegisterInstance(...)100%11100%
RegisterLazy(...)100%11100%

File(s)

/home/runner/work/dotnet-api-diff/dotnet-api-diff/src/DotNetApiDiff/Commands/TypeRegistrar.cs

#LineLine coverage
 1// Copyright DotNet API Diff Project Contributors - SPDX Identifier: MIT
 2using DotNetApiDiff.Interfaces;
 3using Microsoft.Extensions.DependencyInjection;
 4using Microsoft.Extensions.Logging;
 5using Spectre.Console.Cli;
 6using System.Diagnostics.CodeAnalysis;
 7
 8namespace DotNetApiDiff.Commands;
 9
 10/// <summary>
 11/// Type registrar for Spectre.Console.Cli that uses Microsoft.Extensions.DependencyInjection
 12/// </summary>
 13internal sealed class TypeRegistrar : ITypeRegistrar
 14{
 15    private readonly IServiceCollection _services;
 16
 17    /// <summary>
 18    /// Initializes a new instance of the <see cref="TypeRegistrar"/> class.
 19    /// </summary>
 20    /// <param name="services">The service collection.</param>
 521    public TypeRegistrar(IServiceCollection services)
 522    {
 523        _services = services;
 524    }
 25
 26    /// <summary>
 27    /// Initializes a new instance of the <see cref="TypeRegistrar"/> class.
 28    /// </summary>
 29    /// <param name="serviceProvider">The service provider.</param>
 1030    public TypeRegistrar(IServiceProvider serviceProvider)
 1031    {
 1032        _services = new ServiceCollection();
 33
 34        // Add the service provider itself so commands can access it
 1035        _services.AddSingleton(serviceProvider);
 36
 37        // Add logging services from the original provider
 1038        _services.AddSingleton<ILoggerFactory>(provider =>
 1039            serviceProvider.GetRequiredService<ILoggerFactory>());
 1040        _services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
 41
 42        // Add other required services from the original provider
 1043        _services.AddSingleton<IGlobalExceptionHandler>(provider =>
 1044            serviceProvider.GetRequiredService<IGlobalExceptionHandler>());
 1045        _services.AddSingleton<IExitCodeManager>(provider =>
 1046            serviceProvider.GetRequiredService<IExitCodeManager>());
 47
 48        // Add core API services from the original provider
 1049        _services.AddScoped<IAssemblyLoader>(provider =>
 1050            serviceProvider.GetRequiredService<IAssemblyLoader>());
 1051        _services.AddScoped<IApiExtractor>(provider =>
 1052            serviceProvider.GetRequiredService<IApiExtractor>());
 1053        _services.AddScoped<IApiComparer>(provider =>
 1054            serviceProvider.GetRequiredService<IApiComparer>());
 1055        _services.AddScoped<IReportGenerator>(provider =>
 1056            serviceProvider.GetRequiredService<IReportGenerator>());
 57
 58        // Register the CompareCommand to be resolved from the original service provider
 1059        _services.AddTransient<CompareCommand>(provider =>
 060        {
 061            var logger = provider.GetRequiredService<ILogger<CompareCommand>>();
 062            var exitCodeManager = serviceProvider.GetRequiredService<IExitCodeManager>();
 063            var exceptionHandler = serviceProvider.GetRequiredService<IGlobalExceptionHandler>();
 064            return new CompareCommand(serviceProvider, logger, exitCodeManager, exceptionHandler);
 1065        });
 1066    }
 67
 68    /// <summary>
 69    /// Builds the service provider
 70    /// </summary>
 71    /// <returns>The service provider</returns>
 72    public ITypeResolver Build()
 1173    {
 1174        return new TypeResolver(_services.BuildServiceProvider());
 1175    }
 76
 77    /// <summary>
 78    /// Registers a service as a specific type
 79    /// </summary>
 80    /// <param name="service">The service type</param>
 81    /// <param name="implementation">The implementation type</param>
 82    public void Register(Type service, Type implementation)
 4683    {
 4684        _services.AddSingleton(service, implementation);
 4685    }
 86
 87    /// <summary>
 88    /// Registers an instance as a specific type
 89    /// </summary>
 90    /// <param name="service">The service type</param>
 91    /// <param name="implementation">The implementation instance</param>
 92    public void RegisterInstance(Type service, object implementation)
 3393    {
 3394        _services.AddSingleton(service, implementation);
 3395    }
 96
 97    /// <summary>
 98    /// Registers a factory for a specific type
 99    /// </summary>
 100    /// <param name="service">The service type</param>
 101    /// <param name="factory">The factory</param>
 102    public void RegisterLazy(Type service, Func<object> factory)
 10103    {
 10104        _services.AddSingleton(service, _ => factory());
 10105    }
 106}
 107
 108/// <summary>
 109/// Type resolver for Spectre.Console.Cli that uses Microsoft.Extensions.DependencyInjection
 110/// </summary>
 111internal sealed class TypeResolver : ITypeResolver, IDisposable
 112{
 113    private readonly IServiceProvider _provider;
 114
 115    /// <summary>
 116    /// Initializes a new instance of the <see cref="TypeResolver"/> class.
 117    /// </summary>
 118    /// <param name="provider">The service provider.</param>
 119    public TypeResolver(IServiceProvider provider)
 120    {
 121        _provider = provider ?? throw new ArgumentNullException(nameof(provider));
 122    }
 123
 124    /// <summary>
 125    /// Resolves an instance of the specified type
 126    /// </summary>
 127    /// <param name="type">The type to resolve</param>
 128    /// <returns>The resolved instance</returns>
 129    public object? Resolve([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T
 130    {
 131        if (type == null)
 132        {
 133            return null;
 134        }
 135
 136        // Settings classes should be created directly by Spectre.Console, not through DI
 137        // Check both by type name and inheritance to be absolutely sure
 138        if (typeof(CommandSettings).IsAssignableFrom(type) ||
 139            type.Name.EndsWith("CommandSettings") ||
 140            type.BaseType?.Name == "CommandSettings")
 141        {
 142            try
 143            {
 144                return Activator.CreateInstance(type);
 145            }
 146            catch (Exception ex)
 147            {
 148                throw new InvalidOperationException($"Failed to create instance of {type.FullName}. Ensure it has a para
 149            }
 150        }
 151
 152        // Try to resolve through DI first, then fallback to Activator for other types
 153        var service = _provider.GetService(type);
 154        if (service != null)
 155        {
 156            return service;
 157        }
 158
 159        // Fallback to Activator for types not registered in DI
 160        try
 161        {
 162            return Activator.CreateInstance(type);
 163        }
 164        catch (Exception ex)
 165        {
 166            throw new InvalidOperationException($"Could not resolve type '{type.FullName}'. Type is not registered in DI
 167        }
 168    }
 169
 170    /// <summary>
 171    /// Disposes the resolver
 172    /// </summary>
 173    public void Dispose()
 174    {
 175        if (_provider is IDisposable disposable)
 176        {
 177            disposable.Dispose();
 178        }
 179    }
 180}