Skip to content

Commit

Permalink
Eased requirements for custom type resolvers
Browse files Browse the repository at this point in the history
  • Loading branch information
JKamsker committed Mar 14, 2024
1 parent 1f2ea93 commit f689941
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 13 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ public static void Main(string[] args)
}
```

Hint: If you are using a custom `ITypeRegistrar` or `ITypeResolver`, make sure, that the `ITypeResolver` resolves itself correctly. Or use the [JKToolKit.Spectre.Console.Extensions.Hosting-ng](https://github.com/JKamsker/Spectre.Console.Extensions.Hosting-ng) package

## Shell integrations
1. [PowerShell](#powershell)
3. More to come...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.1.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="2.6.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="Shouldly" Version="4.1.0" />
<PackageReference Include="xunit" Version="2.4.2" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
using JKToolKit.Spectre.AutoCompletion.Tests.Utilities.DependencyInjection;

using Microsoft.Extensions.DependencyInjection;

using Spectre.Console.Cli;
using Spectre.Console.Cli.Tests.Data.Commands;
using Spectre.Console.Testing;
using Spectre.Console.Tests.Data;

namespace Spectre.Console.Tests.Unit.Cli;


public sealed partial class CommandAppTests
{
[UsesVerify]
Expand Down Expand Up @@ -1232,5 +1235,35 @@ public void Completion_Should_Suggest_Remaining_Options()
// Assert.Equal("dotnet", args.Runtime);
// Assert.Equal("C:\\Users\\Tool.dll", args.Command);
//}

[Fact]
public void CustomTypeResolver_Works()
{
var services = new ServiceCollection();
var customRegistry = new CustomTypeRegistrar(services);

var fixture = new CommandAppTester(customRegistry);
fixture.Configure(config =>
{
config.AddAutoCompletion();
config.SetApplicationName("myapp");
config.PropagateExceptions();
config.AddBranch("user", feline =>
{
feline.AddCommand<UserAddCommand>("add");
feline.AddCommand<UserSuperAddCommand>("superAdd");
});
});

var commandToRun = Constants.CompleteCommand
.Append("\"myapp user superAdd Josh --gender male \"");

// When
var result = fixture.Run(commandToRun.ToArray());

// Then
Assert.Equal("--age", result.Output);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using Microsoft.Extensions.DependencyInjection;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JKToolKit.Spectre.AutoCompletion.Tests.Utilities.DependencyInjection;

public class CustomTypeRegistrar : ITypeRegistrar
{
private readonly IServiceCollection _builder;

public CustomTypeRegistrar(IServiceCollection builder)
{
_builder = builder;
}

public ITypeResolver Build()
{
return new CustomTypeResolver(_builder.BuildServiceProvider());
}

public void Register(Type service, Type implementation)
{
_builder.AddSingleton(service, implementation);
}

public void RegisterInstance(Type service, object implementation)
{
_builder.AddSingleton(service, implementation);
}

public void RegisterLazy(Type service, Func<object> func)
{
if (func is null)
{
throw new ArgumentNullException(nameof(func));
}

_builder.AddSingleton(service, _ => func());
}
}

public sealed class CustomTypeResolver : ITypeResolver, IDisposable
{
private readonly IServiceProvider _provider;

public CustomTypeResolver(IServiceProvider provider)
{
_provider = provider ?? throw new ArgumentNullException(nameof(provider));
}

public object? Resolve(Type? type)
{
return type == null ? null : _provider.GetService(type);
}

public void Dispose()
{
if (_provider is IDisposable disposable)
{
disposable.Dispose();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

namespace JKToolKit.Spectre.AutoCompletion.Completion.Internals;


public sealed class CompleteCommandSettings : CommandSettings
{
[CommandArgument(0, "[commandToComplete]")]
Expand All @@ -38,7 +37,7 @@ public override ValidationResult Validate()
#if NET5_0_OR_GREATER
var allowedFormats = new[] { "plain", "json", };
#else
var allowedFormats = new[] { "plain", };
var allowedFormats = new[] { "plain", };
#endif
if (!allowedFormats.Contains(Format, StringComparer.OrdinalIgnoreCase))
{
Expand All @@ -59,13 +58,17 @@ public sealed partial class CompleteCommand : AsyncCommand<CompleteCommandSettin
public CompleteCommand
(
DefaultTypeResolver? resolver1 = default,
ITypeResolver? resolver2 = default
ITypeResolver? resolver2 = default,
IConfiguration? configuration = default
)
{
ITypeResolver resolver = resolver1 ?? resolver2 ?? throw new ArgumentNullException(nameof(resolver1));
ITypeResolver? resolver = resolver1 ?? resolver2 ?? TryBuildResolverFromConfiguration(configuration);

var configuration = resolver.Resolve(typeof(IConfiguration)) as IConfiguration;
var commandModel = resolver.Resolve(typeof(CommandModel)) as CommandModel;
CommandModel? commandModel = resolver.Resolve(typeof(CommandModel)) as CommandModel;
if (configuration is null)
{
configuration = resolver.Resolve(typeof(IConfiguration)) as IConfiguration;
}

_model = commandModel ?? throw new ArgumentNullException(nameof(commandModel));
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
Expand All @@ -74,8 +77,36 @@ public CompleteCommand
_writer = configuration.Settings.Console.GetConsole();
}

private static ITypeResolver TryBuildResolverFromConfiguration(IConfiguration? configuration)
{
ITypeResolver? resolver;
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}

var registrar = configuration.Settings.Registrar as TypeRegistrar;
if (registrar is null)
{
throw new ArgumentNullException(nameof(registrar));
}

//var reg = registrar._registrar;
var field = registrar.GetType().GetField("_registrar", BindingFlags.NonPublic | BindingFlags.Instance);
if (field is null)
{
throw new ArgumentNullException(nameof(field));
}

var reg = field.GetValue(registrar) as ITypeRegistrar;
if (reg is null)
{
throw new ArgumentNullException(nameof(reg));
}

resolver = reg.Build();
return resolver;
}

public override async Task<int> ExecuteAsync(
CommandContext context,
Expand Down Expand Up @@ -298,10 +329,10 @@ private async Task<List<CompletionResult>> GetCommandArgumentsAsync(
continue;
}

// var completions = await CompleteCommandOption(
// context.Parent,
// parameter.Parameter,
// parameter.Value);
// var completions = await CompleteCommandOption(
// context.Parent,
// parameter.Parameter,
// parameter.Value);
var completions = await CompleteCommandOption(context, parameter);

if (completions == null || !completions.Suggestions.Any())
Expand Down

0 comments on commit f689941

Please sign in to comment.