'How to refer to the program name in help strings?
In my CLI program the usage examples are provided as part of the help message. Using the clap derive interface I can do the following
#[derive(Parser, Debug, Default)]
#[clap( after_help = "EXAMPLES:\n $ foo abc.txt")]
pub struct CmdLine {...}
The program name foo is hard coded in the literal string above.
How can I avoid hard-coding the program name and get it dynamically; for example, from std::env::args[0] or clap::App:get_bin_name() ?
Solution 1:[1]
clap provides a macro called crate_name! that will take the name from your cargo.toml.
For example, suppose you have this in your cargo.toml.
[package]
name = "myapp"
description = "myapp description"
version = "0.1.0"
edition = "2021"
authors = [ "John Doe" ]
Then, in your application, you can fetch these values using the macros, like this:
let matches = Command::new(clap::crate_name!())
.version(clap::crate_version!())
.author(clap::crate_authors!())
.about(clap::crate_description!())
//
// abbreviated
//
The section below is appended to respond to the original poster's specific question. See the comments below for context. Also, including some learnings as well.
Appended per the discussion in comments.
Based on the comments/discussion below, initial thought is just to stuff the binary name from the arguments into a string and pass into the after_help() function. For example, something like this:
let bin_name = std::env:args().into_iter().next().unwrap();
let matches = Command::new(bin_name)
.after_help(format!("Text that includes {}", bin_name)) // This won't compile
.get_matches();
Taking this approach, you quickly run into a lifetime requirement in the function signature for after_help(). From clap's repo:
pub fn after_help<S: Into<&'help str>>(mut self, help: S)
In fact, if you look, there are many fields in the Command struct that have the lifetime annotation (&'help) on them. The Command::new() method doesn't have this lifetime annotation so it worked fine to just pass it bin_name as shown above.
Below is an abbreviated solution that dynamically generates after-help text in a manner that adheres to the lifetime requirements. Assuming a clean binary (application), called "foo", add the following code:
cargo.toml
[package]
name = "foo"
version = "0.1.0"
description = "A foo cli application"
authors = [ "John Doe" ]
edition = "2021"
[dependencies]
clap = { version = "3.1.6", features = ["cargo"] }
main.rs
fn main() {
// Get the binary name from the command line
let bin_name = std::env::args().into_iter().next().unwrap();
// Construct text that will be used in after_help.
let after_help_text = format!(
"Some after-help text that includes the binary name: {}",
bin_name
);
// clap, by default, will reference the name of your package. So, if you're
// doing the above, you might as well override the usage text too so you're
// being consistent.
let usage_text = format!("{}", bin_name);
if let Err(e) = foo::get_args(bin_name, after_help_text, usage_text).and_then(foo::run) {
eprintln!("{e}");
std::process::exit(1);
}
}
lib.rs
use clap::{ArgMatches, Command};
pub fn get_args(
bin_name: String,
after_help_text: String,
usage_text: String,
) -> std::io::Result<ArgMatches> {
let matches = Command::new(bin_name)
.override_usage(usage_text.as_str())
.version(clap::crate_version!())
.after_help(after_help_text.as_str())
.author(clap::crate_authors!())
.about(clap::crate_description!())
// add and configure args...
.get_matches();
Result::Ok(matches)
}
pub fn run(matches: ArgMatches) -> std::io::Result<()> {
// Do your CLI logic here based on matches.
Ok(())
}
Running the solution ( cargo run -- --help ) will produce the following output:
./foo 0.1.0
John Doe
A foo cli application
USAGE:
./foo
OPTIONS:
-h, --help Print help information
-V, --version Print version information
Some after-help text that includes the binary name: ./foo
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
