Detecting whether help was provided via command line arguments to Spectre.Console
If you code simple application Spectre.Console - then you're interested not only on parsing command line arguments, but also you're interested in figuring out whether --help was used as command line argument.
Using .net reflection it's possible:
https://github.com/tapika/swupd/blob/master/cakebuild/CommandLineArgs.cs#L248
but uses bit ugly syntax for that purpose.
Is there a simpler solution to this ?
Apparently help command is built-in, and quite well hidden.
// Get the command to execute.
var leaf = parsedResult.Tree.GetLeafCommand();
if (leaf.Command.IsBranch || leaf.ShowHelp)
{
// Branches can't be executed. Show help.
configuration.Settings.Console.SafeRender(HelpWriter.WriteCommand(model, leaf.Command));
return leaf.ShowHelp ? 0 : 1;
}
Can't imagine calling this code as user from outside of Spectre.Console.
In theory you can also override default console output, like this:
TestConsole console = new TestConsole();
var actualConsole = AnsiConsole.Console;
AnsiConsole.Console = console;
AnsiConsole.Record();
var app = new CommandApp<ParseCommandLineArgs>();
...
int exitCode = app.Run(args);
actualConsole.Write(AnsiConsole.ExportText());
and then display help at the end. (text will be without colors)
Sounds bit complex solution as well.
The CommandExecutor.Execute(...) method (which the first code snippet above from @tapika was taken from) does explicitly handle the -v|--version argument:
// No default command?
if (model.DefaultCommand == null)
{
// Got at least one argument?
var firstArgument = args.FirstOrDefault();
if (firstArgument != null)
{
// Asking for version? Kind of a hack, but it's alright.
// We should probably make this a bit better in the future.
if (firstArgument.Equals("--version", StringComparison.OrdinalIgnoreCase) ||
firstArgument.Equals("-v", StringComparison.OrdinalIgnoreCase))
{
var console = configuration.Settings.Console.GetConsole();
console.WriteLine(ResolveApplicationVersion(configuration));
return 0;
}
}
}
I do wonder if this kind of explicit handling of command arguments could also be used for the -h|--help help option.
When this issue is triaged/is found to be valid - please assign it to myself and I'm happy to fix.
I would prefer that no need to assume order of arguments - whether help comes first or not.
Previously I have help parameter handling as hardcoded, and it was parsed like this:
var helpOpt = new CommandOptionAttribute("-h|--help");
var isMatch = helpOpt.GetType().GetMethod("IsMatch", BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var arg in args)
{
if ((bool)isMatch.Invoke(helpOpt, new object[] { arg.TrimStart('-') }))
{
displaysHelp = true;
}
}
But this also implies that help command is the same as "-h|--help" - I can easily think that in future such variations as -?|--? could be added to be more flexible. Help option needs to be centralized and preferably within Spectre.Console.
Could this be coded somehow like this:
var (exitCode, helpWasShown) = app.Run(args);
if(helpWasShown) { Environment.Exit(exitCode); }
... continue with processing command line options...
?
Would you propose to also refactor the code snippet above that handles the -v|--version argument into the same approach used for -h|--help, if for no other reason than consistency ?
Generally API's and functionalities they provide:
API1: CommandOptionAttribute :
1. parse command line argument
- bad: not public api
API2: app.Run() :
1. parse command line arguments
2. print help if help requested
- bad: combines two api's, not possible to separate
- bad: does inform end-user that help printing occurred
For API1 - consistency, and simplity to use.
Maybe it would make sense to also refactor API2 to have parse and print as a separate operations, but I think returning two return parameters would be beneficial already.