Description
One of our goals is simplyfing the configuration, making it easier to discover and use.
This can be achieved by making the configuration mutable (no need for a dedicated builder) and making it an optional argument for Command.Parse
(this has already happened).
My current idea is following:
public class CliConfiguration
{
public CliConfiguration(Command command)
{
Command = command;
Directives = new()
{
new SuggestDirective()
// I am not sure whether we should enable other Directives by default
// new EnvironmentVariablesDirective()
// new ParseDirective()
};
}
public Command Command { get; }
public bool EnablePosixBundling { get; set; } = true;
public bool EnableDefaultExceptionHandler { get; set; } = true;
public bool EnableTypoCorrections { get; set; } = false;
public TimeSpan? ProcessTerminationTimeout { get; set; } = TimeSpan.FromSeconds(2);
public int ParseErrorExitCode { get; set; } = 1;
public TryReplaceToken? ResponseFileTokenReplacer { get; set; } = StringExtensions.TryReadResponseFile;
public List<Directive> Directives { get; }
}
How to disable signaling and handling of process termination via a CancellationToken
? Set ProcessTerminationTimeout
to null
.
How to disable default response file token replacer? Set ResponseFileTokenReplacer
to null
.
How to customize exception handler? Set EnableDefaultExceptionHandler
to false
and catch the exceptions on your own:
CliConfiguration config = new(command) { EnableDefaultExceptionHandler = false };
ParseResult parseResult = command.Parse(args, config);
try
{
return parseResult.Invoke();
}
catch (Exception ex)
{
// custom exception handler
}
The alternative for customizing the ParseErrorExitCode
is following:
ParseResult parseResult = command.Parse(args);
if (parseResult.Errors.Any())
{
return $customValue;
}
The most tricky part is how HelpOption
and VersionOption
should be enabled and disabled. Currently, the config builder just adds them to the provided Command
options list:
I don't like it, as it's a side effect. But on the other hand, I don't have an ideal alternative.
Possible solutions:
Expect the users to add them in explicit way.
command.Options.Add(new HelpOption());
command.Options.Add(new VersionOption());
Advantage(s): no magic, everything is crystal clear, perf.
Disadvantage(s): help would be not enabled by default.
Create every command with Help and Version options added by default
public Command(string name, bool addDefaultOptions = true)
{
if (addDefaultOptions)
{
Options.Add(new HelpOption());
Options.Add(new VersionOption());
}
}
Advantage(s): help enabled by default.
Disadvantage(s): Hard to customize help (find, cast & customize or replace) I expect that Version
makes sense only for the root command, it would pollute subcommands.
Perhaps it would make more sense for RootCommand
?
Maybe the argument should not be optional, so everyone who creates a Command would need to think about it?
Expose the options as part of Config type, add them the root Command when parsing
public class CliConfiguration
{
public HelpOption? HelpOption { get; set; } = new ();
public VersionOption? VersionOption { get; set; } = new ();
}
Advantage(s): quite easy to discover and configure, enabled by default.
Disadvantage(s): side effect of adding them to the root command (but so far nobody complained about it beside me?)
Do we really need VersionOption
enabled by default?
Add a list of default Options to Config, similarly to Directives
public class CliConfiguration
{
public List<Option> Options { get; }
}
Advantage(s): quite easy to discover, consistent with Directives
Disadvantage(s): hard to configure a specific option (find HelpOption, cast it and then set the builder), side effect of adding them to the root command
@jonsequitur @KathleenDollard @Keboo @KalleOlaviNiemitalo please provide feedback