Skip to content

Commit

Permalink
Simplified usage of BindingContext resolved command handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
fredrikhr committed Jul 20, 2021
1 parent 98a251b commit d556325
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 5 deletions.
35 changes: 34 additions & 1 deletion src/System.CommandLine.Tests/Invocation/CommandHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.CommandLine.Binding;
using System.CommandLine.Builder;
using System.CommandLine.Invocation;
using System.CommandLine.IO;
using System.CommandLine.Parsing;
Expand Down Expand Up @@ -322,7 +323,6 @@ public async Task Method_parameters_of_type_InvocationContext_receive_the_curren
boundContext.ParseResult.ValueForOption(option).Should().Be(123);
}


private class ExecuteTestClass
{
public string boundName = default;
Expand Down Expand Up @@ -507,5 +507,38 @@ public class OverridenVirtualTestCommandHandler : VirtualTestCommandHandler
public override Task<int> InvokeAsync(InvocationContext context)
=> Task.FromResult(41);
}

[Fact]
public static void FromBindingContext_forwards_invocation_to_bound_handler_type()
{
var command = new RootCommand
{
Handler = CommandHandler.FromBindingContext<BindingContextResolvedCommandHandler>()
};
command.Handler.Should().NotBeOfType<BindingContextResolvedCommandHandler>();
var parser = new CommandLineBuilder(command)
.ConfigureBindingContext(context => context.AddService<BindingContextResolvedCommandHandler>())
.Build();

var console = new TestConsole();
parser.Invoke(Array.Empty<string>(), console);
console.Out.ToString().Should().Be(typeof(BindingContextResolvedCommandHandler).FullName);
}

public class BindingContextResolvedCommandHandler : ICommandHandler
{
public BindingContextResolvedCommandHandler(IConsole console)
{
Console = console;
}

public IConsole Console { get; }

public Task<int> InvokeAsync(InvocationContext context)
{
Console.Out.Write(GetType().FullName);
return Task.FromResult(0);
}
}
}
}
24 changes: 20 additions & 4 deletions src/System.CommandLine/Binding/BindingContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public IConsole Console

internal ServiceProvider ServiceProvider { get; }

public void AddModelBinder(ModelBinder binder) =>
public void AddModelBinder(ModelBinder binder) =>
_modelBindersByValueDescriptor.Add(binder.ValueDescriptor.ValueType, binder);

public ModelBinder GetModelBinder(IValueDescriptor valueDescriptor)
Expand All @@ -67,7 +67,7 @@ public void AddService(Type serviceType, Func<IServiceProvider, object> factory)
{
ServiceProvider.AddService(serviceType, factory);
}

public void AddService<T>(Func<IServiceProvider, T> factory)
{
if (factory is null)
Expand All @@ -78,6 +78,22 @@ public void AddService<T>(Func<IServiceProvider, T> factory)
ServiceProvider.AddService(typeof(T), s => factory(s));
}

public void AddService(Type serviceType)
{
object factory(IServiceProvider serviceProvider)
{
var bindingContext =
serviceProvider.GetService(typeof(BindingContext)) as BindingContext
?? this;
var valueDescriptor = new ModelBinder.AnonymousValueDescriptor(serviceType);
var modelBinder = bindingContext.GetModelBinder(valueDescriptor);
return modelBinder.CreateInstance(bindingContext)!;
}
AddService(serviceType, factory);
}

public void AddService<T>() => AddService(typeof(T));

internal bool TryGetValueSource(
IValueDescriptor valueDescriptor,
[MaybeNullWhen(false)] out IValueSource valueSource)
Expand Down Expand Up @@ -108,8 +124,8 @@ internal bool TryBindToScalarValue(
else
{
var parsed = ArgumentConverter.ConvertObject(
valueDescriptor as IArgument ?? new Argument(valueDescriptor.ValueName),
valueDescriptor.ValueType,
valueDescriptor as IArgument ?? new Argument(valueDescriptor.ValueName),
valueDescriptor.ValueType,
value,
resources);

Expand Down
12 changes: 12 additions & 0 deletions src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,18 @@ public static CommandLineBuilder ConfigureConsole(
return builder;
}

public static CommandLineBuilder ConfigureBindingContext(
this CommandLineBuilder builder,
Action<BindingContext> configureBindingContext)
{
builder.AddMiddleware((context, next) =>
{
configureBindingContext?.Invoke(context.BindingContext);
return next(context);
}, default(MiddlewareOrder));
return builder;
}

public static CommandLineBuilder EnableDirectives(
this CommandLineBuilder builder,
bool value = true)
Expand Down
4 changes: 4 additions & 0 deletions src/System.CommandLine/Invocation/CommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,10 @@ public static ICommandHandler Create<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T1
Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, Task<int>> action) =>
HandlerDescriptor.FromDelegate(action).GetCommandHandler();

public static ICommandHandler FromBindingContext<THandler>()
where THandler : ICommandHandler =>
Create((InvocationContext context, THandler handler) => handler.InvokeAsync(context));

internal static async Task<int> GetExitCodeAsync(object value, InvocationContext context)
{
switch (value)
Expand Down

0 comments on commit d556325

Please sign in to comment.