You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Default values could be part of the core parser layer or part of extensibility as a subsystem. This design proposes making default values a subsystem. This offers more control to subsystem authors so that they can offer more control to CLI authors.
One compelling case is #1191's case for environment variables defaults that are not shown in help - the name of the environment variable, but not the current value is shown.
That issue also discusses environment variables, at least sometimes and maybe always, being able to fulfill the "required" aspect of validation.
The requirement that help display default values will also test our design for subsystems exchanging information :-)
Default values should be able to contribute to help.
Approaches to default values
Defaults may be explicit values or calculated. Each is valuable in different scenarios. There are additional considerations in some calculated scenarios - such as #1191 's discussion of environment variable handling.
In order to provide maximum flexibility and the potential for an evolving API, default values are a subsystem.
The core parser will provide the default for the type as would be returned by default.
Drawbacks
While this approach has benefits, it also means default values will not be available in the (new) simple parsing that uses the core parser directly.
Background: As discussed in #2338, there are two modes of using System.CommandLine (Powderhouse). It can be used as a full-featured parser with a pipeline of subsystems, or a very simple parser with any subsystems explicitly called. Placing default values into a subsystem means they are only available when that subsystem is called, from a pipeline or explicitly.
Discussing this with someone that expects to use simple parsing, they anticipated they would use nullables to indicate missing values and null coalescing to add defaults.
Placing defaults into the pipeline is consistent with simple parsing being raw access to the Posix parsing portions only.
Details for base subsystem
A base DefaultValueSubsystem will ship with subsystems. This describes it's design. Any evolved default value systems should be derived from it.
Initialize
No action.
GetIsActivated
Always returns true.
Execute
Sets values in the value results (options and arguments).
This means the values in results are changeable outside the parser. The raw converted value may also be retained.
Teardown
No action.
Data
The base DefaultValueSubsystem allows setting of the default value in several ways, such as:
An explicit value (as object but with a typecheck?)
A calculated default (like the Func<ValueResult, T> factory)
An enviroment variable (as a string name) (new, may not be at GA)
A dependent default (new, may not be at GA)
The data stored for each type of default is given below.
Order of evaluation
The DefaultValueSubsystem.Execute method would be the first to execute after parsing and after terminating informational subsystems like help and version. It runs before validation.
The order of individual symbol result evaluation should be considered indeterminate and any dependencies handled via dependent delegates.
Explicit value
This would just be the value held as an object. The DefaultValueSubsystem would include:
defaultValueSubsystem.SetEnviroment(mySymbol,name);// or mySymbol.With(defaultValue.Enviroment,name);
Dependent defaults
An example of a dependent default is if one option/argument should be relative to another value (default or user entered). For example, a default --end-date might be 5 days after the --start-date. To correctly calculate defaults, the start date's default must be calcualted before the end date.
One of the things custom parsing was used for was dependent delegates. We think this is sufficiently common to provide a direct solution.
defaultValueSubsystem.SetDependentDefault(mySymbol,newDependentDefault(...));// or mySymbol.With(defaultValue.DependentDefault,newDependentDefault(...));
Help text
The DefaultValueSubsystem would provide default help text as "defaults to " (localized):
Dependent default: $"value calcuated on {}" (localized)
The user may supply explicit help text, which uses the same pattern as the different default patterns, and set via:
defaultValueSubsystem.SetHelpText(mySymbol,text);// or mySymbol.With(defaultValue.HelpText,text);
Explicit help text would be used similar to $"defaults to {text}" (localized)
Future default kinds
Future default kinds might be exciting, because the common cases could be explicit. One common case is adding a value to a set value or dependent value. For example, adding or subtracting a date from the current date or a date in another result.
This may result in generalizing the pattern used in DependentDefault to use a base class which handles HelpText, such as:
"defaults to today + 5" (localized)
"defaults to --start-date + 5" (localized)
This would shift the localization burden for the most common cases from the CLI author to the subsystem, which might be us who has the infrastructure.
The text was updated successfully, but these errors were encountered:
I am still not sure what the precedence should be between calculated values, dependent values, and explicit values. However, since environment variables are closer to the user, they should clearly win and it is a clear use case to have both an environment variable name and one of the other approaches.
If we allowed calculated and dependent values to be conditional, then they should win over explicit values.
Otherwise, it feels like a coin flip on this precedence.
Edit: Updated to TryGetValue and other typos
Default values could be part of the core parser layer or part of extensibility as a subsystem. This design proposes making default values a subsystem. This offers more control to subsystem authors so that they can offer more control to CLI authors.
One compelling case is #1191's case for environment variables defaults that are not shown in help - the name of the environment variable, but not the current value is shown.
That issue also discusses environment variables, at least sometimes and maybe always, being able to fulfill the "required" aspect of validation.
The requirement that help display default values will also test our design for subsystems exchanging information :-)
Default values should be able to contribute to help.
Approaches to default values
Defaults may be explicit values or calculated. Each is valuable in different scenarios. There are additional considerations in some calculated scenarios - such as #1191 's discussion of environment variable handling.
In order to provide maximum flexibility and the potential for an evolving API, default values are a subsystem.
The core parser will provide the default for the type as would be returned by
default
.Drawbacks
While this approach has benefits, it also means default values will not be available in the (new) simple parsing that uses the core parser directly.
Background: As discussed in #2338, there are two modes of using System.CommandLine (Powderhouse). It can be used as a full-featured parser with a pipeline of subsystems, or a very simple parser with any subsystems explicitly called. Placing default values into a subsystem means they are only available when that subsystem is called, from a pipeline or explicitly.
Discussing this with someone that expects to use simple parsing, they anticipated they would use nullables to indicate missing values and null coalescing to add defaults.
Placing defaults into the pipeline is consistent with simple parsing being raw access to the Posix parsing portions only.
Details for base subsystem
A base
DefaultValueSubsystem
will ship with subsystems. This describes it's design. Any evolved default value systems should be derived from it.Initialize
No action.
GetIsActivated
Always returns
true
.Execute
Sets values in the value results (options and arguments).
This means the values in results are changeable outside the parser. The raw converted value may also be retained.
Teardown
No action.
Data
The base
DefaultValueSubsystem
allows setting of the default value in several ways, such as:object
but with a typecheck?)Func<ValueResult, T>
factory)string
name) (new, may not be at GA)The data stored for each type of default is given below.
Order of evaluation
The
DefaultValueSubsystem.Execute
method would be the first to execute after parsing and after terminating informational subsystems like help and version. It runs before validation.The order of individual symbol result evaluation should be considered indeterminate and any dependencies handled via dependent delegates.
Explicit value
This would just be the value held as an object. The DefaultValueSubsystem would include:
to allow setting as:
Calculated default
This provides a factory method for calculating the default. The DefaultValueSubsystem would include:
to allow setting as:
Enviroment variable (as a
string
name) (new, may not be at GA)This provides an environment variable name, whose value would be the default. The DefaultValueSubsystem would include:
to allow setting as:
Dependent defaults
An example of a dependent default is if one option/argument should be relative to another value (default or user entered). For example, a default
--end-date
might be 5 days after the--start-date
. To correctly calculate defaults, the start date's default must be calcualted before the end date.One of the things custom parsing was used for was dependent delegates. We think this is sufficiently common to provide a direct solution.
Dependent defaults could be modeled as:
public interface HelpTextProvider
string HelpText
public class DependentDefault : HelpTextProvider
readonly IEnumerable<CliSymbol> DependsOn
AddDependency(CliSymbol)
abstract virtual CalcuateDefault(IEnumerable<SymbolResult>)
The DefaultValueSubsystem would include:
to allow setting as:
Help text
The
DefaultValueSubsystem
would provide default help text as "defaults to " (localized):The user may supply explicit help text, which uses the same pattern as the different default patterns, and set via:
Explicit help text would be used similar to $"defaults to {text}" (localized)
Future default kinds
Future default kinds might be exciting, because the common cases could be explicit. One common case is adding a value to a set value or dependent value. For example, adding or subtracting a date from the current date or a date in another result.
This may result in generalizing the pattern used in
DependentDefault
to use a base class which handlesHelpText
, such as:"defaults to today + 5" (localized)
"defaults to --start-date + 5" (localized)
This would shift the localization burden for the most common cases from the CLI author to the subsystem, which might be us who has the infrastructure.
The text was updated successfully, but these errors were encountered: