clap icon indicating copy to clipboard operation
clap copied to clipboard

Control if Arg is hidden based on presence of another Arg

Open emersonford opened this issue 7 months ago • 4 comments

Please complete the following tasks

Clap Version

4.5.38

Describe your use case

I have a subcommand with 20+ flags. A small subset of these are common flags, while the rest are for advanced features.

Showing all the flags in --help is quite noisy; I would ideally like to have a --help where only the common flags are shown and a --help-all where all of the flags are shown. There doesn't seem to be a good mechanism to do this today?

The suggestion in #4687 is to use -h and --help, which doesn't work super great for me because the users of my CLI have all gotten accustomed to using --help. Further, it's not particularly ergonomic to write a custom --help flag for myself because my subcommands are defined in separate crates (where I have a main binary crate that depends on each subcommand crate), so there's no way for me to grab a CommandFactory to customize --help.

Describe the solution you'd like

I was thinking of something like hide_unless_present, along the lines of required_unless_present. Then you could write a subcommand like so:

#[derive(Args)]
struct Foo {
    #[arg(long)]
    common_flag: Option<String>,

    #[arg(long, hide_unless_present = "help_all")]
    advanced_flag: Option<String>,

    /// Show advanced feature flags.
    #[arg(long, action = ArgAction::HelpLong)]
    help_all: (),
}

Alternatives, if applicable

No response

Additional Context

No response

emersonford avatar May 24 '25 06:05 emersonford

The multi-crate aspect isn't too clear to me as to why you can't just do

#[derive(Args)]
#[command(disable_help_flag = true)]
struct Foo {
    #[arg(long)]
    common_flag: Option<String>,

    #[arg(long, hide_short_help = true)]
    advanced_flag: Option<String>,

    /// Show basic feature flags.
    #[arg(long, action = ArgAction::HelpShort)]
    help: (),

    /// Show advanced feature flags.
    #[arg(long, action = ArgAction::HelpLong)]
    help_all: (),
}

You might even be able to do

#[derive()]
#[command(disable_help_flag = true)]
struct Cli {
    #[command(subcommand)]
    command: Command,

    /// Show basic feature flags.
    #[arg(long, action = ArgAction::HelpShort, global = true)]
    help: (),

    /// Show advanced feature flags.
    #[arg(long, action = ArgAction::HelpLong, global = true)]
    help_all: (),
}


#[derive(Subcommand)]
enum Command {
    Foo(Foo),
}

#[derive(Args)]
#[command(disable_help_flag = true)]
struct Foo {
    #[arg(long)]
    common_flag: Option<String>,

    #[arg(long, hide_short_help = true)]
    advanced_flag: Option<String>,
}

epage avatar May 26 '25 17:05 epage

I was thinking of something like hide_unless_present, along the lines of required_unless_present. Then you could write a subcommand like so:

The problem with this is that the help action is activated immediately and is not aware of the full command line available.

epage avatar May 26 '25 17:05 epage

The multi-crate aspect isn't too clear to me as to why you can't just do

Ah I brought up the multi-crate aspect because one way you could achieve this is by:

  1. Parse args (that include some --help-all flag)
  2. If --help-all is used, then create a command factory, override hide for a set of arguments, then call print_help on that new Command.

But this only works if you have access to the top-level CommandFactory.

#[arg(long, action = ArgAction::HelpLong, global = true)]

Hmm, suppose this works, though you're still limited to only having two "versions" of the help page. I.e. you could not have something like --help-feature-one and --help-feature-two.

The problem with this is that the help action is activated immediately and is not aware of the full command line available.

Yeah -- the solution I had in mind was to pass just the first argument that triggers HelpLong to write_help. If you wanted some "additive" functionality, that's where hide_unless_present_any comes in.

emersonford avatar May 28 '25 19:05 emersonford

Hmm, suppose this works, though you're still limited to only having two "versions" of the help page. I.e. you could not have something like --help-feature-one and --help-feature-two.

Keep in mind, the original request was only for two forms of help. I'm ok with entertaining the idea of different flavors of help beyond short/long but it shouldn't be theoretical but a case we can concretely talk about and a clear example of why its important or generally applicable.

Generally when I've seen people want more than two topics, they tend to instead have "help topics" in a help subcommand. That is something we don't help with atm. If we went down the route of N flavors of help, I don't feel comfortable with hide_unless_present as I feel that over generalizes into a way that can be confusing for users approaching this as they are likely to assume that arguments after --help can affect --help.

epage avatar May 28 '25 19:05 epage