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

feat: Return help message as String for Command #3874

Closed
wants to merge 11 commits into from
46 changes: 46 additions & 0 deletions src/builder/command.rs
Expand Up @@ -708,6 +708,52 @@ impl<'help> App<'help> {
c.print()
}

/// Returns the help message (`-h`).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move these functions to a consistent location (we currently have short/long help grouped and show print followed by write variants).

///
/// # Examples
///
/// ```rust
/// # use clap::Command;
/// let mut cmd = Command::new("myprog");
/// let help: String = cmd.render_help();
/// print!("{}",help);
/// ```
pub fn render_help(&mut self) -> String {
self._build_self();

let mut help: String = "".into();
let usage = Usage::new(self);
match Help::new(HelpWriter::String(&mut help), self, &usage, false).write_help() {
Ok(_) => help,
Err(e) => {
panic!("{}", e.to_string())
}
}
}

/// Returns the long help message.
///
/// # Examples
///
/// ```rust
/// # use clap::Command;
/// let mut cmd = Command::new("myprog");
/// let help: String = cmd.render_long_help();
/// print!("{}",help);
/// ```
pub fn render_long_help(&mut self) -> String {
self._build_self();

let mut help: String = "".into();
let usage = Usage::new(self);
match Help::new(HelpWriter::String(&mut help), self, &usage, true).write_help() {
Ok(_) => help,
Err(e) => {
panic!("{}", e.to_string())
}
Comment on lines +751 to +753
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should either be returning an error or making it clear why a panic is unexpected to happen. The latter is the case for this. We have a common panic message for these cases (its INTERNAL something I think). We can just write_help().expect(INTERNAL_...)

}
}

/// Prints the long help message (`--help`) to [`io::stdout()`].
///
/// See also [`Command::print_help`].
Expand Down
5 changes: 5 additions & 0 deletions src/output/help.rs
Expand Up @@ -129,6 +129,10 @@ macro_rules! write_method {
Ok(())
}
HelpWriter::Normal(w) => w.write_all($msg.as_ref()),
HelpWriter::String(s) => {
s.push_str($msg.into().as_str());
Ok(())
}
}
};
}
Expand Down Expand Up @@ -1117,6 +1121,7 @@ const TAB_WIDTH: usize = 4;
pub(crate) enum HelpWriter<'writer> {
Normal(&'writer mut dyn Write),
Buffer(&'writer mut Colorizer),
String(&'writer mut String),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is a new HelpWriter needed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was to simplify writing to a String, but from the points you have made it shouldn't be necessary.

}

fn should_show_arg(use_long: bool, arg: &Arg) -> bool {
Expand Down
41 changes: 41 additions & 0 deletions tests/builder/help.rs
Expand Up @@ -606,6 +606,33 @@ SUBCOMMANDS:
sub short about sub
";

static SETUP_HELP_MESSAGE: &str = "test 1.3
Kevin K.
tests stuff

USAGE:
test

OPTIONS:
-h, --help Print help information
-V, --version Print version information
";

static SETUP_LONG_HELP_MESSAGE: &str = "test 1.3
Kevin K.
tests stuff

USAGE:
test

OPTIONS:
-h, --help
Print help information

-V, --version
Print version information
";
Comment on lines +609 to +634
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit; could you move these into the test function bodies?

A lot of older tests do this and the separation between the expected output and the rest of the test function makes it harder to follow. We've been writing new tests with the expected string in the body and updating the old tests piecemeal.


fn setup() -> Command<'static> {
Command::new("test")
.author("Kevin K.")
Expand Down Expand Up @@ -2896,3 +2923,17 @@ fn display_name_subcommand_explicit() {
Some("child.display")
);
}

#[test]
fn get_help_as_string() {
let cmd = &mut setup();
let help = cmd.render_help();
assert_eq!(help, SETUP_HELP_MESSAGE);
}

#[test]
fn get_long_help_as_string() {
let cmd = &mut setup();
let help = cmd.render_long_help();
assert_eq!(help, SETUP_LONG_HELP_MESSAGE);
}