command-line-api icon indicating copy to clipboard operation
command-line-api copied to clipboard

Accessing global options

Open beachwalker opened this issue 3 years ago • 5 comments

I don't see how to handle global options with the new 2.0 API.


         var cmd = //... some Command, the RootCommand is somewhere else created, not accessible here
         var arg = new Argument<string>("x", "Mysterious x");

        cmd.SetHandler(async (string x) =>
            {
                await DoSomething(x);
            },
            someArgument);`

Question: How to access/fill global options in this case?

beachwalker avatar Mar 31 '22 09:03 beachwalker

You'll need to have access to your Option<T> instance that was used as a global option and pass it into SetHandler.

jonsequitur avatar Mar 31 '22 22:03 jonsequitur

I dont think that's going to be a very helpful API when you're building a complex CLI, say you have commands nested 3 layers deep, you're going to be passing alot of dependencies around. If this is set in stone, then I'd maybe go so far as to say the method to AddGlobalOption should be removed if they're not actually "global"

abbottdev avatar Apr 06 '22 20:04 abbottdev

The option is global within the command line interface. Effectively, AddGlobalOption lets you replace calls like this:


var option = new Option<string>("-x");

var rootCommand = new RootCommand
{
    option,
    new Command("subcommand")
    {
        option
    }
};

with something like this:


var option = new Option<string>("-x");

var rootCommand = new RootCommand
{
    new Command("subcommand")
};

rootCommand.AddGlobalOption(option);

when you're building a complex CLI, say you have commands nested 3 layers deep, you're going to be passing alot of dependencies around

Can you give an example?

jonsequitur avatar Apr 07 '22 01:04 jonsequitur

hey @jonsequitur - this is the sort of thing I mean: https://gist.github.com/abbottdev/7bbfd9e59752cd15a93d684e7bad616f

The reason for creating sub-classed commands is because the composability of the commands when you need to specify handlers ends up with a mass of variables - bear in mind this is a simple example :)

With the above ^ I dont see what value adding AddGlobalOption does - if you have to pass the option around anyway as it would be a dependency to all commands that require it, and therefore it's implicitly global.

abbottdev avatar Apr 07 '22 08:04 abbottdev

Thanks, @abbotdev. Your example seems to mix instantiating subcommands within your constructors while passing in the global option. I wonder if a static property on a base class would be more consistent with this OOP style?


public class MyCommand : Command
{
    public static Option<string> envOption = new("--env", "An environment override");

    public MyCommand(string name, string description) : base(name, description) {}
}

public class GenerateMessageCommand : MyCommand
{
    public GenerateMessageCommand() : base("message", "Generates messages")
    {
        this.AddOption(envOption);
        this.AddCommand(new Command("order", "Generates order messages"));
        this.AddCommand(new GenerateStockMessageCommand(envOption));
    }
}

jonsequitur avatar Apr 07 '22 15:04 jonsequitur

Since https://github.com/dotnet/command-line-api/pull/2083, you can also use ParseResult.GetValue<T>(string name) to look up the values of global options, in which case you don't need to carry the Option<T> instance around; but then the compiler cannot verify that you've spelled the name of the option correctly.

KalleOlaviNiemitalo avatar Apr 03 '23 20:04 KalleOlaviNiemitalo