diff --git a/src/Spectre.Console.Cli/Internal/Extensions/TypeRegistrarExtensions.cs b/src/Spectre.Console.Cli/Internal/Extensions/TypeRegistrarExtensions.cs index 109bebf32..b2b149954 100644 --- a/src/Spectre.Console.Cli/Internal/Extensions/TypeRegistrarExtensions.cs +++ b/src/Spectre.Console.Cli/Internal/Extensions/TypeRegistrarExtensions.cs @@ -17,6 +17,12 @@ public static void RegisterDependencies(this ITypeRegistrar registrar, CommandMo throw new InvalidOperationException("Command setting type cannot be null."); } + if (command.SettingsType is { IsAbstract: false, IsClass: true }) + { + // Register the settings type + registrar?.Register(command.SettingsType, command.SettingsType); + } + if (command.CommandType != null) { registrar?.Register(command.CommandType, command.CommandType); diff --git a/test/Directory.Build.props b/test/Directory.Build.props index dc84e5b85..357cc7df7 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -1,6 +1,6 @@ - 10 + 12 false true diff --git a/test/Spectre.Console.Cli.Tests/Spectre.Console.Cli.Tests.csproj b/test/Spectre.Console.Cli.Tests/Spectre.Console.Cli.Tests.csproj index 187d05cb6..db78e3a67 100644 --- a/test/Spectre.Console.Cli.Tests/Spectre.Console.Cli.Tests.csproj +++ b/test/Spectre.Console.Cli.Tests/Spectre.Console.Cli.Tests.csproj @@ -11,6 +11,7 @@ + diff --git a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Injection.Settings.cs b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Injection.Settings.cs new file mode 100644 index 000000000..8541ed915 --- /dev/null +++ b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Injection.Settings.cs @@ -0,0 +1,116 @@ +#nullable enable + +using Microsoft.Extensions.DependencyInjection; + +namespace Spectre.Console.Tests.Unit.Cli; + +public sealed partial class CommandAppTests +{ + public sealed partial class Injection + { + public sealed class Settings + { + public sealed class CustomInheritedCommand : Command + { + private readonly SomeFakeDependency _dep; + + public CustomInheritedCommand(SomeFakeDependency dep) + { + _dep = dep; + } + + public override int Execute(CommandContext context, CustomInheritedCommandSettings settings) + { + return 0; + } + } + + public sealed class SomeFakeDependency + { + } + + public abstract class CustomBaseCommandSettings : CommandSettings + { + } + + public sealed class CustomInheritedCommandSettings : CustomBaseCommandSettings + { + } + + private sealed class CustomTypeRegistrar : ITypeRegistrar + { + private readonly IServiceCollection _services; + + public CustomTypeRegistrar(IServiceCollection services) + { + _services = services; + } + + public ITypeResolver Build() + { + return new CustomTypeResolver(_services.BuildServiceProvider()); + } + + public void Register(Type service, Type implementation) + { + _services.AddSingleton(service, implementation); + } + + public void RegisterInstance(Type service, object implementation) + { + _services.AddSingleton(service, implementation); + } + + public void RegisterLazy(Type service, Func func) + { + _services.AddSingleton(service, provider => func()); + } + } + + public sealed class CustomTypeResolver : ITypeResolver + { + private readonly IServiceProvider _provider; + + public CustomTypeResolver(IServiceProvider provider) + { + _provider = provider ?? throw new ArgumentNullException(nameof(provider)); + } + + public object? Resolve(Type? type) + { + ArgumentNullException.ThrowIfNull(type); + return _provider.GetRequiredService(type); + } + } + + [Fact] + public void Should_Inject_Settings() + { + static CustomTypeRegistrar BootstrapServices() + { + var services = new ServiceCollection(); + services.AddSingleton(); + return new CustomTypeRegistrar(services); + } + + // Given + var app = new CommandAppTester(BootstrapServices()); + + app.Configure(config => + { + config.PropagateExceptions(); + config.AddBranch("foo", b => + { + b.AddCommand("bar"); + }); + }); + + // When + var result = app.Run("foo", "bar"); + + // Then + result.ExitCode.ShouldBe(0); + } + } + } +} \ No newline at end of file diff --git a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Injection.cs b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Injection.cs index b49bbbe3b..23ef6b72f 100644 --- a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Injection.cs +++ b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Injection.cs @@ -2,13 +2,17 @@ namespace Spectre.Console.Tests.Unit.Cli; public sealed partial class CommandAppTests { - public sealed class Injection + public sealed partial class Injection { public sealed class FakeDependency { } - public sealed class InjectSettings : CommandSettings + public abstract class BaseInjectSettings : CommandSettings + { + } + + public sealed class InjectSettings : BaseInjectSettings { public FakeDependency Fake { get; set; } diff --git a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.cs b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.cs index 68be0f5f0..76b7581de 100644 --- a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.cs +++ b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.cs @@ -557,7 +557,7 @@ public void Can_Register_Default_Command_Settings_When_Configuring_Application() // Then registrar.Registrations.ContainsKey(typeof(DogSettings)).ShouldBeTrue(); - registrar.Registrations[typeof(DogSettings)].Count.ShouldBe(1); + registrar.Registrations[typeof(DogSettings)].Count.ShouldBe(2); registrar.Registrations[typeof(DogSettings)].ShouldContain(typeof(DogSettings)); } @@ -587,7 +587,7 @@ public void Can_Register_Command_Settings_When_Configuring_Application() // Then registrar.Registrations.ContainsKey(typeof(DogSettings)).ShouldBeTrue(); - registrar.Registrations[typeof(DogSettings)].Count.ShouldBe(1); + registrar.Registrations[typeof(DogSettings)].Count.ShouldBe(2); registrar.Registrations[typeof(DogSettings)].ShouldContain(typeof(DogSettings)); registrar.Registrations.ContainsKey(typeof(MammalSettings)).ShouldBeTrue(); registrar.Registrations[typeof(MammalSettings)].Count.ShouldBe(1);