Skip to content

Ability to customize the built-in help subcommand #5815

@mr-briggs

Description

@mr-briggs

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-builderArea: Builder APIC-enhancementCategory: Raise on the bar on expectationsS-waiting-on-designStatus: Waiting on user-facing design to be resolved before implementing

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions