< Summary

Information
Class: DotNetApiDiff.Commands.TypeResolver
Assembly: DotNetApiDiff
File(s): /home/runner/work/dotnet-api-diff/dotnet-api-diff/src/DotNetApiDiff/Commands/TypeRegistrar.cs
Line coverage
60%
Covered lines: 20
Uncovered lines: 13
Coverable lines: 33
Total lines: 180
Line coverage: 60.6%
Branch coverage
56%
Covered branches: 9
Total branches: 16
Branch coverage: 56.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

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

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>
 21    public TypeRegistrar(IServiceCollection services)
 22    {
 23        _services = services;
 24    }
 25
 26    /// <summary>
 27    /// Initializes a new instance of the <see cref="TypeRegistrar"/> class.
 28    /// </summary>
 29    /// <param name="serviceProvider">The service provider.</param>
 30    public TypeRegistrar(IServiceProvider serviceProvider)
 31    {
 32        _services = new ServiceCollection();
 33
 34        // Add the service provider itself so commands can access it
 35        _services.AddSingleton(serviceProvider);
 36
 37        // Add logging services from the original provider
 38        _services.AddSingleton<ILoggerFactory>(provider =>
 39            serviceProvider.GetRequiredService<ILoggerFactory>());
 40        _services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
 41
 42        // Add other required services from the original provider
 43        _services.AddSingleton<IGlobalExceptionHandler>(provider =>
 44            serviceProvider.GetRequiredService<IGlobalExceptionHandler>());
 45        _services.AddSingleton<IExitCodeManager>(provider =>
 46            serviceProvider.GetRequiredService<IExitCodeManager>());
 47
 48        // Add core API services from the original provider
 49        _services.AddScoped<IAssemblyLoader>(provider =>
 50            serviceProvider.GetRequiredService<IAssemblyLoader>());
 51        _services.AddScoped<IApiExtractor>(provider =>
 52            serviceProvider.GetRequiredService<IApiExtractor>());
 53        _services.AddScoped<IApiComparer>(provider =>
 54            serviceProvider.GetRequiredService<IApiComparer>());
 55        _services.AddScoped<IReportGenerator>(provider =>
 56            serviceProvider.GetRequiredService<IReportGenerator>());
 57
 58        // Register the CompareCommand to be resolved from the original service provider
 59        _services.AddTransient<CompareCommand>(provider =>
 60        {
 61            var logger = provider.GetRequiredService<ILogger<CompareCommand>>();
 62            var exitCodeManager = serviceProvider.GetRequiredService<IExitCodeManager>();
 63            var exceptionHandler = serviceProvider.GetRequiredService<IGlobalExceptionHandler>();
 64            return new CompareCommand(serviceProvider, logger, exitCodeManager, exceptionHandler);
 65        });
 66    }
 67
 68    /// <summary>
 69    /// Builds the service provider
 70    /// </summary>
 71    /// <returns>The service provider</returns>
 72    public ITypeResolver Build()
 73    {
 74        return new TypeResolver(_services.BuildServiceProvider());
 75    }
 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)
 83    {
 84        _services.AddSingleton(service, implementation);
 85    }
 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)
 93    {
 94        _services.AddSingleton(service, implementation);
 95    }
 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)
 103    {
 104        _services.AddSingleton(service, _ => factory());
 105    }
 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>
 11119    public TypeResolver(IServiceProvider provider)
 11120    {
 11121        _provider = provider ?? throw new ArgumentNullException(nameof(provider));
 11122    }
 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
 7130    {
 7131        if (type == null)
 0132        {
 0133            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
 7138        if (typeof(CommandSettings).IsAssignableFrom(type) ||
 7139            type.Name.EndsWith("CommandSettings") ||
 7140            type.BaseType?.Name == "CommandSettings")
 0141        {
 142            try
 0143            {
 0144                return Activator.CreateInstance(type);
 145            }
 0146            catch (Exception ex)
 0147            {
 0148                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
 7153        var service = _provider.GetService(type);
 7154        if (service != null)
 7155        {
 7156            return service;
 157        }
 158
 159        // Fallback to Activator for types not registered in DI
 160        try
 0161        {
 0162            return Activator.CreateInstance(type);
 163        }
 0164        catch (Exception ex)
 0165        {
 0166            throw new InvalidOperationException($"Could not resolve type '{type.FullName}'. Type is not registered in DI
 167        }
 7168    }
 169
 170    /// <summary>
 171    /// Disposes the resolver
 172    /// </summary>
 173    public void Dispose()
 7174    {
 7175        if (_provider is IDisposable disposable)
 7176        {
 7177            disposable.Dispose();
 7178        }
 7179    }
 180}