Command line argument parsing with Clap

Add this crate to your own project:

cargo add clap --features derive

Clap (derive style)

clap-badge cat-command-line-badge

This application describes the structure of its command-line interface using clap's Derive API. The documentation gives information on the Builder API as well.

In the Derive API, a struct is declared which will contain the data for all the command line options. The clap macros then create all the parsing, validation logic and help for those options automatically. The short and long options control the flag the user will be expected to type; short flags look like -c and long flags look like --count.

use clap::Parser;

/// Simple program to greet a person
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
    /// Name of the person to greet
    #[clap(short, long)]
    name: Option<String>,  // Make 'name' required by removing Option<>

    /// Number of times to greet
    #[clap(short, long, default_value_t = 1)]
    count: u8,
}

fn main() {
    let args = Args::parse();

    let users_name = match args.name {
        Some(name) => name,
        None => "Mysterious User".to_string(),
    };
    for _ in 0..args.count {
        println!("Hello {}!", users_name)
    }
}

An argument can be made optional by either providing a default value to clap (as is the case for the --count option), or by declaring that argument to be of the Option<> type, such as Option<String> as used with name. In that case, the args.name will be either Some() which holds a String value, or None, so the match expression is used to assign a default name if none was provided.

Usage information is generated by clap macro. The usage for the example application looks like this. Run on the command line:

cargo run --example clap_basic -- --help

The --example argument is used to select the clap_basic program in the examples directory to be run. The -- is needed to separate the arguments passed to the cargo run command from the arguments passed to clap_basic command.

arguments 0.1.0
Simple program to greet a person

USAGE:
    clap_basic [OPTIONS]

OPTIONS:
    -c, --count <COUNT>    Number of times to greet [default: 1]
    -h, --help             Print help information
    -n, --name <NAME>      Name of the person to greet
    -V, --version          Print version information

We can test the application by running a command like the following.

cargo run --example clap_basic -- --name Zoo -c 3

The output is:

Hello Zoo!
Hello Zoo!
Hello Zoo!

Clap (builder style)

clap-badge cat-command-line-badge

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.

The code here requires clap version 4. Use clap = "4" in your Cargo.toml to get the correct version.

In the builder style, each possible argument is described by an Arg struct. The string given to Arg::new() is the internal name of the argument. The short and long options control the flag the user will be expected to type; short flags look like -f and long flags look like --file.

The get_one() method is used to get an argument's value. It returns Some(&value) if the argument was supplied by the user, else None.

The use of PathBuf is to allow file paths which are legal in Linux and MacOS, but not in Rust UTF-8 strings. This is best practice: one encounters such paths quite rarely in practice, but when it happens it is really frustrating without this.

use std::path::PathBuf;

use clap::{Arg, Command, builder::PathBufValueParser};

fn main() {
    let matches = Command::new("My Test Program")
        .version("0.1.0")
        .about("Teaches argument parsing")
        .arg(Arg::new("file")
                 .short('f')
                 .long("file")
                 .help("A cool file")
                 .value_parser(PathBufValueParser::default()))
        .arg(Arg::new("num")
                 .short('n')
                 .long("number")
                 .help("Five less than your favorite number"))
        .get_matches();

    let default_file = PathBuf::from("input.txt");
    let myfile: &PathBuf = matches.get_one("file").unwrap_or(&default_file);
    println!("The file passed is: {}", myfile.display());

    let num_str: Option<&String> = matches.get_one("num");
    match num_str {
        None => println!("No idea what your favorite number is."),
        Some(s) => {
            let parsed: Result<i32, _> = s.parse();
            match parsed {
                Ok(n) => println!("Your favorite number must be {}.", n + 5),
                Err(_) => println!("That's not a number! {}", s),
            }
        }
    }
}

Usage information is generated by clap -h. The usage for the example application looks like this.

Teaches argument parsing

Usage: clap-cookbook [OPTIONS]

Options:
  -f, --file <file>   A cool file
  -n, --number <num>  Five less than your favorite number
  -h, --help          Print help
  -V, --version       Print version

We can test the application by running a command like the following.

$ cargo run --example clap_with_pathbuf.rs -- -f myfile.txt -n 251

The output is:

The file passed is: myfile.txt
Your favorite number must be 256.