Skip to content

Commit 9e4d60b

Browse files
authored
Introduce CliAction (#2095)
* replace ICommandHandler with CliAction * introduce first internal types that derive from CliAction * In case of async invocation EnvironmentVariablesDirective should invoke async command handler and pass the cancellation token * rename Handler to Action everywhere * expose Action only for Command, Directive and Option *expose SetAction only for Command, make it an actual Action (no exit code returning)
1 parent 42c5853 commit 9e4d60b

File tree

53 files changed

+567
-435
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+567
-435
lines changed

samples/HostingPlayground/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ private static CommandLineBuilder BuildCommandLine()
3232
IsRequired = true
3333
}
3434
};
35-
root.Handler = CommandHandler.Create<GreeterOptions, IHost>(Run);
35+
root.Action = CommandHandler.Create<GreeterOptions, IHost>(Run);
3636
return new CommandLineBuilder(root);
3737
}
3838

src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_NamingConventionBinder_api_is_not_changed.approved.txt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@ System.CommandLine.NamingConventionBinder
1717
public static System.Void AddModelBinder(this System.CommandLine.Binding.BindingContext bindingContext, ModelBinder binder)
1818
public static System.CommandLine.Binding.BindingContext GetBindingContext(this System.CommandLine.Invocation.InvocationContext ctx)
1919
public static ModelBinder GetOrCreateModelBinder(this System.CommandLine.Binding.BindingContext bindingContext, System.CommandLine.Binding.IValueDescriptor valueDescriptor)
20-
public abstract class BindingHandler, System.CommandLine.ICommandHandler
20+
public abstract class BindingHandler : System.CommandLine.CliAction
2121
public System.CommandLine.Binding.BindingContext GetBindingContext(System.CommandLine.Invocation.InvocationContext invocationContext)
22-
public System.Int32 Invoke(System.CommandLine.Invocation.InvocationContext context)
23-
public System.Threading.Tasks.Task<System.Int32> InvokeAsync(System.CommandLine.Invocation.InvocationContext context, System.Threading.CancellationToken cancellationToken = null)
2422
public System.CommandLine.Binding.BindingContext SetBindingContext(System.CommandLine.Binding.BindingContext bindingContext)
2523
public static class CommandHandler
2624
public static BindingHandler Create(System.Delegate delegate)
@@ -115,7 +113,7 @@ System.CommandLine.NamingConventionBinder
115113
.ctor()
116114
public System.Void BindMemberFromValue<TValue>(Expression<Func<TModel,TValue>> property, System.CommandLine.Binding.IValueDescriptor valueDescriptor)
117115
public System.Void BindMemberFromValue<TValue>(Expression<Func<TModel,TValue>> property, Func<System.CommandLine.Binding.BindingContext,TValue> getValue)
118-
public class ModelBindingCommandHandler : BindingHandler, System.CommandLine.ICommandHandler
116+
public class ModelBindingCommandHandler : BindingHandler
119117
public System.Void BindParameter(System.Reflection.ParameterInfo param, System.CommandLine.Argument argument)
120118
public System.Void BindParameter(System.Reflection.ParameterInfo param, System.CommandLine.Option option)
121119
public System.Int32 Invoke(System.CommandLine.Invocation.InvocationContext context)

src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,15 @@ System.CommandLine
3535
public static Argument<System.IO.DirectoryInfo> AcceptExistingOnly(this Argument<System.IO.DirectoryInfo> argument)
3636
public static Argument<System.IO.FileSystemInfo> AcceptExistingOnly(this Argument<System.IO.FileSystemInfo> argument)
3737
public static Argument<T> AcceptExistingOnly<T>(this Argument<T> argument)
38+
public abstract class CliAction
39+
public System.Int32 Invoke(System.CommandLine.Invocation.InvocationContext context)
40+
public System.Threading.Tasks.Task<System.Int32> InvokeAsync(System.CommandLine.Invocation.InvocationContext context, System.Threading.CancellationToken cancellationToken = null)
3841
public class Command : Symbol, System.Collections.Generic.IEnumerable<Symbol>, System.Collections.IEnumerable
3942
.ctor(System.String name, System.String description = null)
43+
public CliAction Action { get; set; }
4044
public System.Collections.Generic.ICollection<System.String> Aliases { get; }
4145
public System.Collections.Generic.IList<Argument> Arguments { get; }
4246
public System.Collections.Generic.IEnumerable<Symbol> Children { get; }
43-
public ICommandHandler Handler { get; set; }
4447
public System.Collections.Generic.IList<Option> Options { get; }
4548
public System.Collections.Generic.IList<Command> Subcommands { get; }
4649
public System.Boolean TreatUnmatchedTokensAsErrors { get; set; }
@@ -50,8 +53,8 @@ System.CommandLine
5053
public System.Collections.Generic.IEnumerator<Symbol> GetEnumerator()
5154
public ParseResult Parse(System.Collections.Generic.IReadOnlyList<System.String> args, CommandLineConfiguration configuration = null)
5255
public ParseResult Parse(System.String commandLine, CommandLineConfiguration configuration = null)
53-
public System.Void SetHandler(System.Func<System.CommandLine.Invocation.InvocationContext,System.Int32> handler)
54-
public System.Void SetHandler(System.Func<System.CommandLine.Invocation.InvocationContext,System.Threading.CancellationToken,System.Threading.Tasks.Task<System.Int32>> handler)
56+
public System.Void SetAction(System.Action<System.CommandLine.Invocation.InvocationContext> action)
57+
public System.Void SetAction(System.Func<System.CommandLine.Invocation.InvocationContext,System.Threading.CancellationToken,System.Threading.Tasks.Task> action)
5558
public static class CommandExtensions
5659
public static System.Int32 Invoke(this Command command, System.String[] args, IConsole console = null)
5760
public static System.Int32 Invoke(this Command command, System.String commandLine, IConsole console = null)
@@ -104,17 +107,14 @@ System.CommandLine
104107
public static System.Void Write(this IConsole console, System.String value)
105108
public static System.Void WriteLine(this IConsole console, System.String value)
106109
public class Directive : Symbol
107-
.ctor(System.String name, System.Func<System.CommandLine.Invocation.InvocationContext,System.Int32> syncHandler = null, System.Func<System.CommandLine.Invocation.InvocationContext,System.Threading.CancellationToken,System.Threading.Tasks.Task<System.Int32>> asyncHandler = null)
110+
.ctor(System.String name)
111+
public CliAction Action { get; set; }
108112
public System.Collections.Generic.IEnumerable<System.CommandLine.Completions.CompletionItem> GetCompletions(System.CommandLine.Completions.CompletionContext context)
109-
public System.Void SetAsynchronousHandler(System.Func<System.CommandLine.Invocation.InvocationContext,System.Threading.CancellationToken,System.Threading.Tasks.Task<System.Int32>> handler)
110-
public System.Void SetSynchronousHandler(System.Func<System.CommandLine.Invocation.InvocationContext,System.Int32> handler)
111113
public class EnvironmentVariablesDirective : Directive
112114
.ctor()
113-
public interface ICommandHandler
114-
public System.Int32 Invoke(System.CommandLine.Invocation.InvocationContext context)
115-
public System.Threading.Tasks.Task<System.Int32> InvokeAsync(System.CommandLine.Invocation.InvocationContext context, System.Threading.CancellationToken cancellationToken = null)
116115
public interface IConsole : System.CommandLine.IO.IStandardError, System.CommandLine.IO.IStandardIn, System.CommandLine.IO.IStandardOut
117116
public abstract class Option : Symbol, System.CommandLine.Binding.IValueDescriptor
117+
public CliAction Action { get; set; }
118118
public System.Collections.Generic.ICollection<System.String> Aliases { get; }
119119
public System.Boolean AllowMultipleArgumentsPerToken { get; set; }
120120
public System.Boolean AppliesToSelfAndChildren { get; set; }
@@ -140,6 +140,7 @@ System.CommandLine
140140
public class ParseDirective : Directive
141141
.ctor(System.Int32 errorExitCode = 1)
142142
public class ParseResult
143+
public CliAction Action { get; }
143144
public System.CommandLine.Parsing.CommandResult CommandResult { get; }
144145
public CommandLineConfiguration Configuration { get; }
145146
public System.Collections.Generic.IReadOnlyList<System.CommandLine.Parsing.ParseError> Errors { get; }

src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Simple.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,10 @@ private static RootCommand BuildCommand()
3333
stringOption
3434
};
3535

36-
command.SetHandler(ctx =>
36+
command.SetAction(ctx =>
3737
{
3838
bool boolean = ctx.ParseResult.GetValue(boolOption);
3939
string text = ctx.ParseResult.GetValue(stringOption);
40-
41-
return 0;
4240
});
4341

4442
return command;

src/System.CommandLine.DragonFruit/CommandLine.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ public static void ConfigureFromMethod(
168168
command.Arguments.Add(ArgumentBuilder.CreateArgument(argsParam));
169169
}
170170

171-
command.Handler = CommandHandler.Create(method, target);
171+
command.Action = CommandHandler.Create(method, target);
172172
}
173173

174174
public static CommandLineBuilder ConfigureHelpFromXmlComments(

src/System.CommandLine.Generator/CommandHandlerSourceGenerator.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace System.CommandLine.Generator
1111
[Generator]
1212
public class CommandHandlerSourceGenerator : ISourceGenerator
1313
{
14-
private const string ICommandHandlerType = "System.CommandLine.ICommandHandler";
14+
private const string CliActionType = "System.CommandLine.CliAction";
1515

1616
public void Execute(GeneratorExecutionContext context)
1717
{
@@ -74,7 +74,7 @@ private static void GenerateHandlerClass(
7474
int handlerCount)
7575
{
7676
builder.Append($@"
77-
private class GeneratedHandler_{handlerCount} : {ICommandHandlerType}
77+
private class GeneratedHandler_{handlerCount} : {CliActionType}
7878
{{
7979
public GeneratedHandler_{handlerCount}(
8080
{invocation.DelegateType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} method");
@@ -115,10 +115,10 @@ private class GeneratedHandler_{handlerCount} : {ICommandHandlerType}
115115
}
116116

117117
builder.Append($@"
118-
public int Invoke(global::System.CommandLine.Invocation.InvocationContext context) => InvokeAsync(context, global::System.Threading.CancellationToken.None).GetAwaiter().GetResult();");
118+
public override int Invoke(global::System.CommandLine.Invocation.InvocationContext context) => InvokeAsync(context, global::System.Threading.CancellationToken.None).GetAwaiter().GetResult();");
119119

120120
builder.Append($@"
121-
public async global::System.Threading.Tasks.Task<int> InvokeAsync(global::System.CommandLine.Invocation.InvocationContext context, global::System.Threading.CancellationToken cancellationToken)
121+
public override async global::System.Threading.Tasks.Task<int> InvokeAsync(global::System.CommandLine.Invocation.InvocationContext context, global::System.Threading.CancellationToken cancellationToken)
122122
{{");
123123
builder.Append($@"
124124
{invocation.InvokeContents()}");
@@ -170,7 +170,7 @@ private static void GenerateSetHandler(
170170
builder.Append(@"
171171
{");
172172
builder.Append($@"
173-
command.Handler = new GeneratedHandler_{handlerCount}(method");
173+
command.Action = new GeneratedHandler_{handlerCount}(method");
174174

175175
if (methodParameters.Length > 0)
176176
{

src/System.CommandLine.Hosting.Tests/HostingHandlerTest.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -134,17 +134,17 @@ public static async Task Invokes_DerivedClass()
134134
service.StringValue.Should().Be("TEST");
135135
}
136136

137-
public abstract class MyBaseHandler : ICommandHandler
137+
public abstract class MyBaseHandler : CliAction
138138
{
139139
public int IntOption { get; set; } // bound from option
140140
public IConsole Console { get; set; } // bound from DI
141141

142-
public int Invoke(InvocationContext context)
142+
public override int Invoke(InvocationContext context)
143143
{
144144
return Act();
145145
}
146146

147-
public Task<int> InvokeAsync(InvocationContext context, CancellationToken cancellationToken)
147+
public override Task<int> InvokeAsync(InvocationContext context, CancellationToken cancellationToken)
148148
{
149149
return Task.FromResult(Act());
150150
}
@@ -159,7 +159,7 @@ public MyCommand() : base(name: "mycommand")
159159
Options.Add(new Option<int>("--int-option")); // or nameof(Handler.IntOption).ToKebabCase() if you don't like the string literal
160160
}
161161

162-
public class MyHandler : ICommandHandler
162+
public class MyHandler : CliAction
163163
{
164164
private readonly MyService service;
165165

@@ -171,13 +171,13 @@ public MyHandler(MyService service)
171171
public int IntOption { get; set; } // bound from option
172172
public IConsole Console { get; set; } // bound from DI
173173

174-
public int Invoke(InvocationContext context)
174+
public override int Invoke(InvocationContext context)
175175
{
176176
service.Value = IntOption;
177177
return IntOption;
178178
}
179179

180-
public Task<int> InvokeAsync(InvocationContext context, CancellationToken cancellationToken)
180+
public override Task<int> InvokeAsync(InvocationContext context, CancellationToken cancellationToken)
181181
{
182182
service.Value = IntOption;
183183
return Task.FromResult(IntOption);
@@ -209,7 +209,7 @@ public MyOtherCommand() : base(name: "myothercommand")
209209
Arguments.Add(new Argument<string>("One") { Arity = ArgumentArity.ZeroOrOne });
210210
}
211211

212-
public class MyHandler : ICommandHandler
212+
public class MyHandler : CliAction
213213
{
214214
private readonly MyService service;
215215

@@ -223,9 +223,9 @@ public MyHandler(MyService service)
223223

224224
public string One { get; set; }
225225

226-
public int Invoke(InvocationContext context) => InvokeAsync(context, CancellationToken.None).GetAwaiter().GetResult();
226+
public override int Invoke(InvocationContext context) => InvokeAsync(context, CancellationToken.None).GetAwaiter().GetResult();
227227

228-
public Task<int> InvokeAsync(InvocationContext context, CancellationToken cancellationToken)
228+
public override Task<int> InvokeAsync(InvocationContext context, CancellationToken cancellationToken)
229229
{
230230
service.Value = IntOption;
231231
service.StringValue = One;

src/System.CommandLine.Hosting.Tests/HostingTests.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ void Execute(IHost host)
2828
}
2929

3030
var config = new CommandLineBuilder(
31-
new RootCommand { Handler = CommandHandler.Create<IHost>(Execute) }
31+
new RootCommand { Action = CommandHandler.Create<IHost>(Execute) }
3232
)
3333
.UseHost()
3434
.Build();
@@ -76,7 +76,7 @@ void Execute(IHost host)
7676
}
7777

7878
var config = new CommandLineBuilder(
79-
new RootCommand { Handler = CommandHandler.Create<IHost>(Execute) }
79+
new RootCommand { Action = CommandHandler.Create<IHost>(Execute) }
8080
)
8181
.UseHost()
8282
.Build();
@@ -108,7 +108,7 @@ void Execute(IHost host)
108108
var config = new CommandLineBuilder(
109109
new RootCommand
110110
{
111-
Handler = CommandHandler.Create<IHost>(Execute),
111+
Action = CommandHandler.Create<IHost>(Execute),
112112
})
113113
.UseHost(host =>
114114
{
@@ -145,7 +145,7 @@ void Execute(IHost host)
145145
var config = new CommandLineBuilder(
146146
new RootCommand
147147
{
148-
Handler = CommandHandler.Create<IHost>(Execute),
148+
Action = CommandHandler.Create<IHost>(Execute),
149149
})
150150
.UseHost(args =>
151151
{
@@ -184,7 +184,7 @@ void Execute(IHost host)
184184
var config = new CommandLineBuilder(
185185
new RootCommand
186186
{
187-
Handler = CommandHandler.Create<IHost>(Execute)
187+
Action = CommandHandler.Create<IHost>(Execute)
188188
})
189189
.UseHost()
190190
.Build();
@@ -203,7 +203,7 @@ public static void UseHost_binds_parsed_arguments_to_options()
203203

204204
var rootCmd = new RootCommand();
205205
rootCmd.Options.Add(new Option<int>($"-{nameof(MyOptions.MyArgument)}"));
206-
rootCmd.Handler = CommandHandler.Create((IHost host) =>
206+
rootCmd.Action = CommandHandler.Create((IHost host) =>
207207
{
208208
options = host.Services
209209
.GetRequiredService<IOptions<MyOptions>>()

src/System.CommandLine.Hosting/HostingExtensions.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public static OptionsBuilder<TOptions> BindCommandLine<TOptions>(
8888

8989
public static IHostBuilder UseCommandHandler<TCommand, THandler>(this IHostBuilder builder)
9090
where TCommand : Command
91-
where THandler : ICommandHandler
91+
where THandler : CliAction
9292
{
9393
return builder.UseCommandHandler(typeof(TCommand), typeof(THandler));
9494
}
@@ -100,9 +100,9 @@ public static IHostBuilder UseCommandHandler(this IHostBuilder builder, Type com
100100
throw new ArgumentException($"{nameof(commandType)} must be a type of {nameof(Command)}", nameof(handlerType));
101101
}
102102

103-
if (!typeof(ICommandHandler).IsAssignableFrom(handlerType))
103+
if (!typeof(CliAction).IsAssignableFrom(handlerType))
104104
{
105-
throw new ArgumentException($"{nameof(handlerType)} must implement {nameof(ICommandHandler)}", nameof(handlerType));
105+
throw new ArgumentException($"{nameof(handlerType)} must implement {nameof(CliAction)}", nameof(handlerType));
106106
}
107107

108108
if (builder.Properties[typeof(InvocationContext)] is InvocationContext invocation
@@ -114,10 +114,10 @@ public static IHostBuilder UseCommandHandler(this IHostBuilder builder, Type com
114114
services.AddTransient(handlerType);
115115
});
116116

117-
BindingHandler bindingHandler = CommandHandler.Create(handlerType.GetMethod(nameof(ICommandHandler.InvokeAsync)));
117+
BindingHandler bindingHandler = CommandHandler.Create(handlerType.GetMethod(nameof(CliAction.InvokeAsync)));
118118
// NullBindingHandler that accumulated services registered so far, before handler creation
119-
bindingHandler.SetBindingContext(command.Handler is BindingHandler pre ? pre.GetBindingContext(invocation) : null);
120-
command.Handler = bindingHandler;
119+
bindingHandler.SetBindingContext(command.Action is BindingHandler pre ? pre.GetBindingContext(invocation) : null);
120+
command.Action = bindingHandler;
121121
bindingHandler.GetBindingContext(invocation).AddService(handlerType, c => c.GetService<IHost>().Services.GetService(handlerType));
122122
}
123123

0 commit comments

Comments
 (0)