Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically register command settings #1463

Merged
merged 1 commit into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion test/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup Label="Settings">
<LangVersion>10</LangVersion>
<LangVersion>12</LangVersion>
<IsPackable>false</IsPackable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<ItemGroup>
<PackageReference Include="IsExternalInit" Version="1.0.3" PrivateAssets="all" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Shouldly" Version="4.2.1" />
<PackageReference Include="Spectre.Verify.Extensions" Version="22.3.1" />
<PackageReference Include="Verify.Xunit" Version="23.2.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -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<CustomInheritedCommandSettings>
{
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<object> 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<SomeFakeDependency, SomeFakeDependency>();
return new CustomTypeRegistrar(services);
}

// Given
var app = new CommandAppTester(BootstrapServices());

app.Configure(config =>
{
config.PropagateExceptions();
config.AddBranch<CustomBaseCommandSettings>("foo", b =>
{
b.AddCommand<CustomInheritedCommand>("bar");
});
});

// When
var result = app.Run("foo", "bar");

// Then
result.ExitCode.ShouldBe(0);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down
4 changes: 2 additions & 2 deletions test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

Expand Down Expand Up @@ -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);
Expand Down