Skip to content

Commit 33d1fb0

Browse files
committed
experiment in avoiding name-based matching on handler invocation
1 parent aefbf35 commit 33d1fb0

File tree

6 files changed

+88
-2
lines changed

6 files changed

+88
-2
lines changed

src/System.CommandLine.Tests/Binding/ModelBindingCommandHandlerTests.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.CommandLine.Binding;
66
using System.CommandLine.Invocation;
77
using System.CommandLine.IO;
8+
using System.CommandLine.Parsing;
89
using System.IO;
910
using System.Linq;
1011
using System.Reflection;
@@ -458,6 +459,46 @@ public async Task Handler_method_receives_command_arguments_bound_to_the_specifi
458459
c.AssertBoundValue(boundValue);
459460
}
460461

462+
[Fact]
463+
public void EXPERIMENT()
464+
{
465+
var stringOption = new Option<string>("--string");
466+
var intOption = new Option<int>("--int");
467+
var filesArg = new Argument<FileInfo[]>();
468+
var wasCalled = false;
469+
var command = new RootCommand
470+
{
471+
stringOption,
472+
intOption,
473+
filesArg
474+
};
475+
476+
command.Handler = CommandHandler.Create(
477+
stringOption, intOption, filesArg,
478+
(s, i, fs) =>
479+
{
480+
wasCalled = true;
481+
482+
s.Should().Be("hello");
483+
i.Should().Be(123);
484+
fs.Select(f => f.Name)
485+
.Should()
486+
.BeEquivalentTo(
487+
"1.txt",
488+
"2.txt",
489+
"3.txt"
490+
);
491+
492+
return Task.CompletedTask;
493+
});
494+
495+
var parser = new Parser(command);
496+
497+
parser.Invoke("--int 123 --string hello 1.txt 2.txt 3.txt");
498+
499+
wasCalled.Should().BeTrue();
500+
}
501+
461502
private static void CaptureMethod<T>(T value, InvocationContext invocationContext)
462503
{
463504
invocationContext.InvocationResult = new BoundValueCapturer(value);

src/System.CommandLine/Argument{T}.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
// Copyright (c) .NET Foundation and contributors. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
using System.CommandLine.Binding;
45
using System.CommandLine.Parsing;
56

67
namespace System.CommandLine
78
{
8-
public class Argument<T> : Argument
9+
public class Argument<T> : Argument, IValueDescriptor<T>
910
{
1011
public Argument()
1112
{

src/System.CommandLine/Binding/IValueDescriptor.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,9 @@ public interface IValueDescriptor
1313

1414
object? GetDefaultValue();
1515
}
16+
17+
public interface IValueDescriptor<T> : IValueDescriptor
18+
{
19+
// FIX: (IValueDescriptor)
20+
}
1621
}

src/System.CommandLine/Invocation/CommandHandler.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,34 @@ public static ICommandHandler Create<T1, T2, T3, T4, T5, T6, T7>(
139139
Func<T1, T2, T3, T4, T5, T6, T7, Task<int>> action) =>
140140
HandlerDescriptor.FromDelegate(action).GetCommandHandler();
141141

142+
public static ICommandHandler Create<T1, T2, T3>(
143+
IValueDescriptor<T1> symbol1,
144+
IValueDescriptor<T2> symbol2,
145+
IValueDescriptor<T3> symbol3,
146+
Func<T1, T2, T3, Task> handle)
147+
{
148+
return new AnonymousCommandHandler(async context =>
149+
{
150+
await handle(
151+
context.ParseResult.ValueFor(symbol1),
152+
context.ParseResult.ValueFor(symbol2),
153+
context.ParseResult.ValueFor(symbol3));
154+
});
155+
}
156+
157+
private class AnonymousCommandHandler : ICommandHandler
158+
{
159+
private readonly Func<InvocationContext, Task> _getResult;
160+
161+
public AnonymousCommandHandler(Func<InvocationContext, Task> getResult)
162+
{
163+
_getResult = getResult;
164+
}
165+
166+
public async Task<int> InvokeAsync(InvocationContext context) =>
167+
await GetResultCodeAsync(_getResult(context), context);
168+
}
169+
142170
internal static async Task<int> GetResultCodeAsync(object value, InvocationContext context)
143171
{
144172
switch (value)

src/System.CommandLine/Option{T}.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
// Copyright (c) .NET Foundation and contributors. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
using System.CommandLine.Binding;
45
using System.CommandLine.Parsing;
56

67
namespace System.CommandLine
78
{
8-
public class Option<T> : Option
9+
public class Option<T> : Option, IValueDescriptor<T>
910
{
1011
public Option(
1112
string alias,

src/System.CommandLine/Parsing/ParseResult.cs

Lines changed: 10 additions & 0 deletions
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.Collections.Generic;
5+
using System.CommandLine.Binding;
56
using System.Diagnostics.CodeAnalysis;
67
using System.Linq;
78

@@ -133,6 +134,15 @@ public T ValueForOption<T>(Option option)
133134
return default!;
134135
}
135136

137+
[return: MaybeNull]
138+
internal T ValueFor<T>(IValueDescriptor<T> symbol) =>
139+
symbol switch
140+
{
141+
Argument<T> argument => ValueForArgument(argument),
142+
Option<T> option => ValueForOption(option),
143+
_ => throw new ArgumentOutOfRangeException()
144+
};
145+
136146
public SymbolResult? this[string alias] => CommandResult.Children[alias];
137147

138148
public override string ToString() => $"{nameof(ParseResult)}: {this.Diagram()}";

0 commit comments

Comments
 (0)