Skip to content

Commit d556325

Browse files
committed
Simplified usage of BindingContext resolved command handlers
1 parent 98a251b commit d556325

File tree

4 files changed

+70
-5
lines changed

4 files changed

+70
-5
lines changed

src/System.CommandLine.Tests/Invocation/CommandHandlerTests.cs

+34-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
using System.CommandLine.Binding;
5+
using System.CommandLine.Builder;
56
using System.CommandLine.Invocation;
67
using System.CommandLine.IO;
78
using System.CommandLine.Parsing;
@@ -322,7 +323,6 @@ public async Task Method_parameters_of_type_InvocationContext_receive_the_curren
322323
boundContext.ParseResult.ValueForOption(option).Should().Be(123);
323324
}
324325

325-
326326
private class ExecuteTestClass
327327
{
328328
public string boundName = default;
@@ -507,5 +507,38 @@ public class OverridenVirtualTestCommandHandler : VirtualTestCommandHandler
507507
public override Task<int> InvokeAsync(InvocationContext context)
508508
=> Task.FromResult(41);
509509
}
510+
511+
[Fact]
512+
public static void FromBindingContext_forwards_invocation_to_bound_handler_type()
513+
{
514+
var command = new RootCommand
515+
{
516+
Handler = CommandHandler.FromBindingContext<BindingContextResolvedCommandHandler>()
517+
};
518+
command.Handler.Should().NotBeOfType<BindingContextResolvedCommandHandler>();
519+
var parser = new CommandLineBuilder(command)
520+
.ConfigureBindingContext(context => context.AddService<BindingContextResolvedCommandHandler>())
521+
.Build();
522+
523+
var console = new TestConsole();
524+
parser.Invoke(Array.Empty<string>(), console);
525+
console.Out.ToString().Should().Be(typeof(BindingContextResolvedCommandHandler).FullName);
526+
}
527+
528+
public class BindingContextResolvedCommandHandler : ICommandHandler
529+
{
530+
public BindingContextResolvedCommandHandler(IConsole console)
531+
{
532+
Console = console;
533+
}
534+
535+
public IConsole Console { get; }
536+
537+
public Task<int> InvokeAsync(InvocationContext context)
538+
{
539+
Console.Out.Write(GetType().FullName);
540+
return Task.FromResult(0);
541+
}
542+
}
510543
}
511544
}

src/System.CommandLine/Binding/BindingContext.cs

+20-4
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public IConsole Console
5151

5252
internal ServiceProvider ServiceProvider { get; }
5353

54-
public void AddModelBinder(ModelBinder binder) =>
54+
public void AddModelBinder(ModelBinder binder) =>
5555
_modelBindersByValueDescriptor.Add(binder.ValueDescriptor.ValueType, binder);
5656

5757
public ModelBinder GetModelBinder(IValueDescriptor valueDescriptor)
@@ -67,7 +67,7 @@ public void AddService(Type serviceType, Func<IServiceProvider, object> factory)
6767
{
6868
ServiceProvider.AddService(serviceType, factory);
6969
}
70-
70+
7171
public void AddService<T>(Func<IServiceProvider, T> factory)
7272
{
7373
if (factory is null)
@@ -78,6 +78,22 @@ public void AddService<T>(Func<IServiceProvider, T> factory)
7878
ServiceProvider.AddService(typeof(T), s => factory(s));
7979
}
8080

81+
public void AddService(Type serviceType)
82+
{
83+
object factory(IServiceProvider serviceProvider)
84+
{
85+
var bindingContext =
86+
serviceProvider.GetService(typeof(BindingContext)) as BindingContext
87+
?? this;
88+
var valueDescriptor = new ModelBinder.AnonymousValueDescriptor(serviceType);
89+
var modelBinder = bindingContext.GetModelBinder(valueDescriptor);
90+
return modelBinder.CreateInstance(bindingContext)!;
91+
}
92+
AddService(serviceType, factory);
93+
}
94+
95+
public void AddService<T>() => AddService(typeof(T));
96+
8197
internal bool TryGetValueSource(
8298
IValueDescriptor valueDescriptor,
8399
[MaybeNullWhen(false)] out IValueSource valueSource)
@@ -108,8 +124,8 @@ internal bool TryBindToScalarValue(
108124
else
109125
{
110126
var parsed = ArgumentConverter.ConvertObject(
111-
valueDescriptor as IArgument ?? new Argument(valueDescriptor.ValueName),
112-
valueDescriptor.ValueType,
127+
valueDescriptor as IArgument ?? new Argument(valueDescriptor.ValueName),
128+
valueDescriptor.ValueType,
113129
value,
114130
resources);
115131

src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs

+12
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,18 @@ public static CommandLineBuilder ConfigureConsole(
143143
return builder;
144144
}
145145

146+
public static CommandLineBuilder ConfigureBindingContext(
147+
this CommandLineBuilder builder,
148+
Action<BindingContext> configureBindingContext)
149+
{
150+
builder.AddMiddleware((context, next) =>
151+
{
152+
configureBindingContext?.Invoke(context.BindingContext);
153+
return next(context);
154+
}, default(MiddlewareOrder));
155+
return builder;
156+
}
157+
146158
public static CommandLineBuilder EnableDirectives(
147159
this CommandLineBuilder builder,
148160
bool value = true)

src/System.CommandLine/Invocation/CommandHandler.cs

+4
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,10 @@ public static ICommandHandler Create<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T1
282282
Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, Task<int>> action) =>
283283
HandlerDescriptor.FromDelegate(action).GetCommandHandler();
284284

285+
public static ICommandHandler FromBindingContext<THandler>()
286+
where THandler : ICommandHandler =>
287+
Create((InvocationContext context, THandler handler) => handler.InvokeAsync(context));
288+
285289
internal static async Task<int> GetExitCodeAsync(object value, InvocationContext context)
286290
{
287291
switch (value)

0 commit comments

Comments
 (0)