Command Line Subcommands in Rust

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.

查看推荐产品

Comments powered by Disqus