dotnet / command-line-api

Command line parsing, invocation, and rendering of terminal output.
https://github.com/dotnet/command-line-api/wiki
MIT License
3.4k stars 381 forks source link

Define a default command #1296

Open dan-vasilov opened 3 years ago

dan-vasilov commented 3 years ago

Is it possible to define a default command? For example, I define a tool named regtool, with the commands "register" and "unregister", and I want the "register" command to be the default. More precisely, if the user types regtool, the utility knows it has to execute the command "register".

twastvedt commented 3 years ago

Looks like you can do this by just assigning the same handler both to a sub command and to the root command itself:

        private static int Main(string[] args)
        {
            ICommandHandler defaultHandler = CommandHandler.Create<string>(async (option) => await Run(true, option).ConfigureAwait(false));

            var rootCommand = new RootCommand
            {
                new Command("Default")
                {
                    Handler = defaultHandler,
                },
                new Command("Other")
                {
                    Handler = CommandHandler.Create<string>(Other),
                },
            };

            rootCommand.Handler = defaultHandler;  // <--

            rootCommand.AddGlobalOption(
                new Option<string>(
                    "--option"));

            return rootCommand.InvokeAsync(args).Result;
        }
nitz commented 3 years ago

I've discovered this works, but not very well. It doesn't benefit from being a fully defined command in the same way, particularly with the way binding validation is handled.

Say your default command, had an argument defined as such:

        defaultCommand.AddArgument(new Argument<FileInfo>("input")
        {
            Description = "The input file(s)",
            Arity = ArgumentArity.OneOrMore,
        }.ExistingOnly());

        // elsewhere
        public class MyConfig {
            public FileInfo Input { get; set; }
        }

If you were to invoke dotnet YourTool.dll Default nonexistantfile, you'd get an error message the likes of:

File does not exist: nonexistantfile

// help info printed here...

But, with the root command's handler pointing to the defaultHandler like you've done there, and invoking the tool like dotnet YourTool.dll, it results in the handler being invoked, with MyConfig.Input being null, where as before it never would have gotten to that point.

If there's a way to completely forward a "no command specified" to a specific command, it seems there needs to be more than just sharing the handler.

bcalco commented 2 years ago

I would like to +1 this so that a first-class concept of a default command is introduced.