Command Line Subcommands in Rust
Here’s the translation of the Go code to Rust, with explanations in Markdown format suitable for Hugo:
use std::env;
use std::process;
fn main() {
// We declare subcommands using separate functions, and proceed to define
// new arguments specific for each subcommand.
let foo_matches = clap::Command::new("foo")
.arg(clap::Arg::new("enable")
.long("enable")
.help("enable")
.action(clap::ArgAction::SetTrue))
.arg(clap::Arg::new("name")
.long("name")
.help("name")
.default_value(""))
.get_matches();
// For a different subcommand we can define different supported arguments.
let bar_matches = clap::Command::new("bar")
.arg(clap::Arg::new("level")
.long("level")
.help("level")
.default_value("0"))
.get_matches();
// The subcommand is expected as the first argument to the program.
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("expected 'foo' or 'bar' subcommands");
process::exit(1);
}
// Check which subcommand is invoked.
match args[1].as_str() {
// For every subcommand, we parse its own arguments and
// have access to trailing positional arguments.
"foo" => {
println!("subcommand 'foo'");
println!(" enable: {}", foo_matches.get_flag("enable"));
println!(" name: {}", foo_matches.get_one::<String>("name").unwrap());
println!(" tail: {:?}", foo_matches.get_many::<String>("").unwrap().collect::<Vec<_>>());
}
"bar" => {
println!("subcommand 'bar'");
println!(" level: {}", bar_matches.get_one::<String>("level").unwrap());
println!(" tail: {:?}", bar_matches.get_many::<String>("").unwrap().collect::<Vec<_>>());
}
_ => {
println!("expected 'foo' or 'bar' subcommands");
process::exit(1);
}
}
}
To run the program, compile it using rustc
:
$ rustc command_line_subcommands.rs
First, invoke the foo subcommand:
$ ./command_line_subcommands foo --enable --name=joe a1 a2
subcommand 'foo'
enable: true
name: joe
tail: ["a1", "a2"]
Now try bar:
$ ./command_line_subcommands bar --level 8 a1
subcommand 'bar'
level: 8
tail: ["a1"]
But bar won’t accept foo’s flags:
$ ./command_line_subcommands bar --enable a1
error: unexpected argument '--enable' found
Usage: bar [OPTIONS]
For more information, try '--help'.
In this Rust version, we’re using the clap
crate for parsing command-line arguments, which is a popular choice in the Rust ecosystem. The clap
crate provides a more declarative way to define command-line interfaces compared to the manual parsing done in the original Go example.
The structure of the program remains similar: we define subcommands with their respective arguments, parse the command-line input, and then handle each subcommand separately. The main differences are in the syntax and the use of Rust-specific constructs like match
for control flow and the Result
type for error handling.
Next, we’ll look at environment variables, another common way to parameterize programs.