Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to customize the built-in help subcommand #5815

Open
2 tasks done
mr-briggs opened this issue Nov 13, 2024 · 2 comments
Open
2 tasks done

Ability to customize the built-in help subcommand #5815

mr-briggs opened this issue Nov 13, 2024 · 2 comments
Labels
A-builder Area: Builder API C-enhancement Category: Raise on the bar on expectations S-waiting-on-design Status: Waiting on user-facing design to be resolved before implementing

Comments

@mr-briggs
Copy link

Please complete the following tasks

Clap Version

4.5.20

Describe your use case

It's currently fairly cumbersome to alter the about message for auto-generated help subcommands when using clap-derive. Consider the following example scenario:

use clap::{command, CommandFactory, FromArgMatches, Parser, Subcommand};

#[derive(Parser)]
pub struct Cli {
    #[command(subcommand)]
    pub command: Option<Commands>,
}

#[derive(Subcommand)]
pub enum Commands {
    Subcmd,
}

To use the parser as-is, we can run something like:

fn main() {
    let cli = Cli::parse();
    match &cli.command { /* ... */ }
}

But if we'd like to customize the about message for the generated help subcommand, we can no longer use the nice parse methods on Cli, and instead need to do something like:

fn main() -> Result<(), clap::Error> {
    let mut cmd = Cli::command();
    cmd.build(); // Need to build `cmd` to generate the `help` subcommand

    let cmd = cmd.mut_subcommand("help", |help_cmd| {
        help_cmd.about("A custom help message")
    });

    // Now we need to manually do what the `parse()` method would do, since we have a modified `Command`:
    let mut matches = cmd.get_matches();
    let cli = Cli::from_arg_matches(&mut matches)?;

    match &cli.command { /* ... */ }

    Ok(())
}

And for the sake of completeness, if we now run this with cargo run -- help, we'd get:

Usage: clap_example [COMMAND]

Commands:
  subcmd  
  help    A custom help message

Options:
  -h, --help  Print help

Describe the solution you'd like

One way to streamline this would be to provide a build() method on Command which returns self, so that we can directly call mut_subcommand in the #[command()] macro. For example, if Command had something like:

#[cfg(feature = "derive")]
pub fn build_for_derive(mut self) -> Self {
    self.build();
    self
}

Then we could do the following:

#[derive(Parser)]
#[command(
    build_for_derive(),
    mut_subcommand("help", |subcmd| subcmd.about("A custom help message"),
)]
pub struct Cli {
    #[command(subcommand)]
    pub command: Option<Commands>,
}

// ... `enum Commands` elided

fn main() {
    // And now we can go back the straightforward setup:
    let cli = Cli::parse();
    match &cli.command { /* ... */ }
}

Note that trying to use build() in place of the build_for_derive() above doesn't work, as build() mutates the Command in-place and doesn't return anything, which breaks the Parser derive macro.

Alternatives, if applicable

No response

Additional Context

No response

@mr-briggs mr-briggs added the C-enhancement Category: Raise on the bar on expectations label Nov 13, 2024
@mr-briggs mr-briggs changed the title Add a build method to Command which returns self Add a build method to Command which returns self Nov 13, 2024
@epage
Copy link
Member

epage commented Nov 13, 2024

Note that changing things after a build is undefined from the APIs perspective. This isn't directly called out in #2911 but is related to that.

What we need is a way for users to get the built-in help behavior when they provide their own help. Help flags started by us auto-detecting a help flag being present (without us being told it shouldn't exist) and then keying off of that. We then switched to the ArgAction system so you could attach a Help action to any flag. Huh, I thought I had done some exploring on this idea but can only find #3773 which alludes to commands not being supported but thats it.

This could also be helpful for #1553.

Potentially related

@epage epage changed the title Add a build method to Command which returns self Ability to customize the built-in help subcommand Nov 13, 2024
@epage epage added A-builder Area: Builder API S-waiting-on-design Status: Waiting on user-facing design to be resolved before implementing labels Nov 13, 2024
@mr-briggs
Copy link
Author

Oh interesting, thank you for the insight about build.

I had initially began this issue as "add help_about and version_about to Command" but after some digging, found that they did actually briefly exist back when it was called App. They were removed a few months later in favour of using mut_arg to modify them after building the App (see this comment), but I'm not sure how things have changed since then.

What we need is a way for users to get the built-in help behavior when they provide their own help.

Huh, that's probably more understandable for users, rather than modifying an auto-gen'd command that they might not even know about. One downside of specifying your own help command though is that it becomes a possible match arm (at least when deriving Subcommand on an enum) even though it's not a valid option under most circumstances (assuming the user-defined help would have the same behaviour of returning Err(ErrorKind::DisplayHelp ...).

That's possibly not a major concern though, compared to the upside of being able to customize the help message.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-builder Area: Builder API C-enhancement Category: Raise on the bar on expectations S-waiting-on-design Status: Waiting on user-facing design to be resolved before implementing
Projects
None yet
Development

No branches or pull requests

2 participants