Command-line argument parsing
Recipe | Crates | Categories |
---|---|---|
Using clap's builder API | ||
Using clap 's derive API | ||
lexopt | ||
pico-args |
Using clap's builder API
This application describes the structure of its command-line interface using clap
⮳'s builder style. The documentation⮳ gives two other possible ways to instantiate an application.
In the builder style, with_name
is the unique identifier that value_of
will use to retrieve the value passed. The clap::Arg::short
⮳ and clap::Arg::long
⮳ options control the flag the user will be expected to type; short flags look like -f
and long flags look like --file
.
use std::path::PathBuf; use clap::value_parser; use clap::Arg; use clap::Command; fn cli() -> Command { clap::Command::new("My Test Program") .bin_name("test_app") .version("0.1.0") .author("Hackerman Jones <hckrmnjones@hack.gov>") .about("Teaches argument parsing") // First possible argument: --num or -n .arg(Arg::new("num") .short('n') // -n argument .long("number") // --number long-form argument .value_name("NUMBER") // placeholder for the argument's value in the help message / usage. .required(false) .help("Enter your favorite number")) // Second possible argument: --file or -f .arg(Arg::new("file") .short('f') .long("file") .value_parser(value_parser!(PathBuf)) .help("Enter the path of a file")) // You can also use the arg! macro: .arg(clap::arg!(-c --config <CONFIG> // "Optionally sets a config file to use")) } fn main() { let matches = cli().get_matches_from(["test_app", "-n", "42", "--file", "README.md"]); // In a real program, use the following to retrieve arguments from the // command line: let matches = cli().get_matches(); if let Some(num) = matches.get_one::<String>("num") { println!("Value for num: {num}"); } if let Some(file_path) = matches.get_one::<PathBuf>("file") { println!("Value for file: {}", file_path.display()); } }
Usage information is generated by clap
⮳. The usage for the example application looks like this.
My Test Program 0.1.0
Hackerman Jones <hckrmnjones@hack.gov>
Teaches argument parsing
USAGE:
testing [OPTIONS]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-f, --file <file> A cool file
-n, --number <num> Five less than your favorite number
We can test the application by running a command like the following.
cargo run -- -f myfile.txt -n 251
The output is:
The file passed is: myfile.txt
Your favorite number must be 256.
Using clap
's derive API
use std::path::PathBuf; use anyhow::Result; use clap::Parser; use clap::Subcommand; // The struct declaring the desired command-line arguments and // commands // The `derive` feature flag is required (see Cargo.toml). #[derive(Parser, Debug)] // Reads the following attributes the from the package's `Cargo.toml` // Alternatively, use #[command(name = "MyApp")] ... #[command(author, version, about, long_about = None)] // Displays Help if no arguments are provided #[command(arg_required_else_help = true)] pub struct Cli { // Positional argument example /// The pattern to look for (the doc comment appears in the help) pattern: Option<String>, /// Required argument example (with default value and validation) #[arg(default_value_t = 8080)] #[arg(value_parser = clap::value_parser!(u16).range(1..))] port: u16, // Named argument example: the path to the file to look into #[arg(short, long)] path: Option<PathBuf>, /// Count example: turn debugging information on #[arg(short, long, action = clap::ArgAction::Count)] debug: u8, // // Alternatively, use the // // [clap-verbosity-flag](https://docs.rs/clap-verbosity-flag/) crate: // // It adds the following flags through the entire program: // // -q silences output // // -v show warnings // // -vv show info // // -vvv show debug // // -vvvv show trace // // By default, this will only report errors. // #[clap(flatten)] // verbose: clap_verbosity_flag::Verbosity, // Subcommands #[command(subcommand)] pub command: Option<Commands>, } // The subcommands #[derive(Subcommand, Debug)] pub enum Commands { /// Read something #[command(arg_required_else_help = true)] Read { /// A boolean flag #[arg(short, long)] all: bool, #[arg(required = true)] file: Vec<PathBuf>, }, /// Say something Tell, } fn main() -> Result<()> { // `Clap` returns a Cli struct populated from `std::env::args_os()`... let cli = Cli::try_parse()?; // You also could use `parse()` // The argument values we got... println!("Path: {:?}", cli.path); // Use `unwrap_or` to set defaults for optional arguments // (or use `default_value_t`` as above). println!("Pattern: {:?}", cli.pattern.unwrap_or("".to_string())); // You can see how many times a particular flag or argument occurred // Note, only flags can have multiple occurrences match cli.debug { 0 => println!("Debug mode is off"), 1 => println!("Debug mode is kind of on"), 2 => println!("Debug mode is on"), _ => println!("Don't be crazy"), } // // Alternatively, use the `verbose` flag, if configured above // env_logger::Builder::new() // .filter_level(cli.verbose.log_level_filter()) // .init(); // Check for the existence of subcommands match &cli.command { Some(Commands::Read { all, file }) => { if *all { println!("Read all..."); } else { println!("Read just one..."); } println!("{:?}", file); } Some(Commands::Tell) => { println!("{}", 42); } None => {} } Ok(()) }
See also
lexopt
Fast compile times, fast runtime, pedantic about correctness. API is less ergonomic
pico-args
Fast compile times, fast runtime, more lax about correctness. API is more ergonomic