70 %
Chris Biscardi

Building a Rust CLI with subcommands using structopt and clap

I have a pre-existing CLI that uses subcommands. In this case it's named toast and the subcommand I care about is toast incremental. Using structopt we can derive our arguments from an enum to generate subcommands:

rust
use std::path::PathBuf;
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[structopt(name = "toast", about = "The best place to stack your JAM")]
enum Toast {
Incremental {
/// Activate debug mode
// short and long flags (-d, --debug) will be deduced from the field's name
#[structopt(short, long)]
debug: bool,
/// Input file
#[structopt(parse(from_os_str))]
input_dir: PathBuf,
/// Output file, stdout if not present
#[structopt(parse(from_os_str))]
output_dir: Option<PathBuf>,
},
}
fn main() {
let opt = Toast::from_args();
println!("{:?}", opt);
}

This will yield us a command that can be run as such:

shell
toast incremental
error: The following required arguments were not provided:
<input-dir>
USAGE:
toast incremental [FLAGS] <input-dir> [output-dir]
For more information try --help

Note that the input is required, so we have to run it with the input directory in this case.

➜ ./target/debug/toast incremental ./test-toast-site
Incremental { debug: false, input_dir: "./test-toast-site", output_dir: None }

The result is a struct that we can do whatever we want with from here.

rust
Incremental {
debug: false,
input_dir: "./test-toast-site",
output_dir: None
}