-
Notifications
You must be signed in to change notification settings - Fork 385
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes to invocation to remove middleware pipeline #2048
Comments
Is Having both Does InvocationResult2.Handler have to synchronously return int, or can it return Task<int>? I haven't really needed async here but I'm surprised if you remove the feature. If the command line has a Is Invoke(Configuration2, ParseResult2, InvocationResult2) a static method, an instance method on InvocationContext, or both? How is the InvokeResult<MyCommand> instance constructed; does that involve reflection when no source generator is available?
|
Thank you for the air code. If I'm understanding correctly, this has the look of shifting from an aspnet middleware construct to In the "examples to get you started", will such code be implemented as static methods (as demonstrated here) or behind some interface, a. la IActionFilter? Finally, is it a correct assumption that there will still be an async path? |
I like the idea, but I wonder if we really need to expose custom result types that derive from
command-line-api/src/System.CommandLine/ParseResult.cs Lines 62 to 65 in 6462288
To support active Options and Directives, we could extend public class ParseResult
{
public SymbolResult SymbolResult { get; }
} and bring back or just public class ParseResult
{
public Symbol Symbol { get; }
} Then our users could do sth like this: public static int Invoke(ParseResult parseResult)
{
return parseResult.Symbol switch
{
HelpOption => DisplayHelp(parseResult),
VersionOption => DisplayVersion(),
ParseDirective => DiagramInput(parseResult),
ParseErrorReporting => DisplayValidationErrors(configuration, parseResult),
Command cmd => InvokeCommand(cmd, parseResult),
_ => throw...
}
} We would not need to worry about creating these types, as they have already been created by the user and provided for the parser. It would require making few internal types public (not a big deal, we should consider doing that regardless of that) and implementing |
@adamsitnik, treating ParseErrorReporting as a Symbol doesn't feel right to me. |
So Invoke should either check ParseResult.Errors first public static int Invoke(ParseResult parseResult)
{
if (parseResult.Errors.Count != 0)
{
return DisplayValidationErrors(configuration, parseResult);
}
return parseResult.Symbol switch
{
HelpOption => DisplayHelp(parseResult),
VersionOption => DisplayVersion(),
ParseDirective => DiagramInput(parseResult),
Command cmd => InvokeCommand(cmd, parseResult),
_ => throw…
};
} or use more complex patterns public static int Invoke(ParseResult parseResult)
{
return parseResult switch
{
{ Symbol: HelpOption } => DisplayHelp(parseResult),
{ Symbol: VersionOption } => DisplayVersion(),
{ Symbol: ParseDirective } => DiagramInput(parseResult),
{ Errors: { Count: not 0 } } => DisplayValidationErrors(configuration, parseResult),
{ Symbol: Command cmd } => InvokeCommand(cmd, parseResult),
_ => throw…
};
} but the former would cause Errors to take precedence over ParseDirective, and the latter would be more verbose and more work to port to C# 7 for supported use on .NET Framework. |
@KalleOlaviNiemitalo Thanks for the suggestion of not having ParseErrorResult as an action, but as something that happens around the action. I think there are some interesting possibilities there, including separate display for some commands and future support for warnings and warnings as errors. On whether we have different ParseResults: I do not want to couple to Symbol. I see several issues here, including that I want folks to use a different help option (a different name, or less/more aliases for example) without changing the invocation. I would be find if the switch was based on something more abstract. As an example, if we had well known integers or strings for the things we understand how to do (the things in our switch). That still leaves open the fact that different information would be used for the different switch arms. I am still thinking about that. |
I wanted to record a few things from a conversation this morning: We think there are three kinds of things at invocation that is somewhat orthogonal to what the Symbol is:
I now think that there may be a fourth:
Spectre.Console uses a separate concept for non-invokable commands (Branch)
As @KalleOlaviNiemitalo pointed out in this issue, there is also the scenario where the state of the result indicates a request for a different action ( |
This is my representation of a bunch of conversations that have happened in imagining a replacement to the middleware pipeline. If you customize the middleware pipeline, please read this and imagine your use case with this approach - or ask us about it. Thanks to @jonsequitur, @adamsitnik, @Keboo, @patriksvensson, @HowardvanRooijen and @mhutch for the conversations that got us this far and to you for feedback, if you customize middleware. If you do not customize middleware, everything will work as before.
Execution changes
We are planning significant changes to the internals of how System.CommandLine determines what to execute.
Why the change
The current System.CommandLine internal execution uses a middleware pipeline which you can alter. There is a default so many programmers have been able to ignore the internal design. That design is a series of steps that can do work and then indicate whether they terminate that runs after parsing on invocation. FOr example, the
UseParseErrorReporting
step displays validation issues and if there are issues does not call the next step in the pipeline - thus terminating.A few problems with this approach:
While these things have proved problematic, the middleware approach has been helpful in us gaining insight into what most people want to do.
One purpose of this issue is to find scenarios we do not yet understand.
This experience, and requests as part of generation and from other sources, has led to a deep redesign. For example, we have confidence in the set of features that are part of the common set:
RegisterWithDotnetSuggest
andUseTokenReplacer
were part ofUseDefaults
, but not of the pipeline.Terminating means that no other actions will be performed. Only one terminating action is ever performed. The non-terminating middleware elements affect the behavior of other steps. This follows a Chain of Responsibility design pattern.
The new design
You'll notice that all of the middleware steps either perform an action and terminate, or modifies another step. Both may also alter the result. To better model this, the new design focuses on the steps - the actions.
For example, help will continue to be configured as a default and will alter the command tree to add the help global option. You will be able to turn that off via configuration and you will still be alter how help is displayed.
Since I think in code, I'll describe how invocation will work cia code; this is not indicating an implementation! Among other things, the naming is designed for this document not implementation, and extra parameters are passed for clarity, and aspects are strictly air code. The number two clarifies that these are modified classes, similar to but not identical to the previous classes. The parse result will know what action is to be performed, in this air code, it is done by classes derived from ParseResult2:
There will obviously be more code to support this, but we think it will solve the problems with the previous approach.
Modifying the pipeline
If you are modifying the pipeline today, you may need to modify the way you think about your solution. Here are some examples to get you started. This is still air code, but relatively close to what you will need do.
Replace version or another step
Do something before or after invocation
Do something for a specific command
Summary
We are redesigning the execution pipeline. While other changes will probably affect you, this change only affects people that have modified the middleware pipeline. In all the cases we have considered, this change will make your code easier to understand and probably reduces the code you need to write; for example we may be able to remove the
CommandLineBuilder
based APIs. We also know that folks have been quite creative with the middleware pipeline and we need you to consider how the new design will work with your needs. If it doesn't, or you have questions, we look forward to hearing from you.The text was updated successfully, but these errors were encountered: