简介

clap-rs是类似于 Python 标准库中argparse的一个命令行解析,参数和子命令解析器。

安装

  • 使用cargo安装clap-rs
cargo add clap
  • 或者可以在Cargo.toml中手动添加
clap = "3.2"

用法

clap-rs提供派生(derive)和生成器(builder)两种使用方式。

派生

使用派生时,可以通过结构体(struct)创建命令行应用。

需要添加使用derive特性。

  • 使用cargo添加clap时,带上需要的特性。
cargo add clap --features derive
  • 或者在Cargo.toml中添加。
clap = { version = "3.2", features = ["derive"]}
use std::path::PathBuf;

use clap::{Parser, Subcommand};

/// 这里clap配置省略了作者、版本等信息
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
    // 使用Option表示这个参数是可选的,value_parser表示解析方式
    #[clap(value_parser)]
    name: Option<String>,

    // short和long表示命令选项,有-c和-config两种方式。
    // value_name可以指定在帮助提示中,参数值的显示名称:
    // --config <FILE>
    #[clap(short, long, value_parser, value_name = "FILE")]
    config: Option<PathBuf>,

    // 这里绑定了解析命令,可以计算参数出现的次数
    #[clap(short, long, action = clap::ArgAction::Count)]
    debug: u8,

    // 子命令
    #[clap(subcommand)]
    command: Option<Commands>,
}

#[derive(Subcommand)]
enum Commands {
    Test {
        // 测试列举
        #[clap(short, long, action)]
        list: bool,
    },
}

fn main() {
    let cli = Cli::parse();

    // 用if let检测选项或者参数
    if let Some(name) = cli.name.as_deref() {
        println!("Value for name: {}", name);
    }

    if let Some(config_path) = cli.config.as_deref() {
        println!("Value for config: {}", config_path.display());
    }

    // 可以显示选项出现的次数
    // 比如-dd对应2,输出Debug mode is on
    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"),
    }

    // 检测子命令,注意bool型这里的解引用。
    match &cli.command {
        Some(Commands::Test { list }) => {
            if *list {
                println!("Printing testing lists...");
            } else {
                println!("Not printing testing lists...");
            }
        }
        None => {}
    }
}

生成器

生成器方式是clap里默认的方式,不需要导入其他特性。

跟派生不同的是,生成器使用链式函数和宏命令生成应用。

匹配参数和解析部分大致相同,不再赘述。

use std::path::PathBuf;

use clap::{arg, command, value_parser, ArgAction, Command};

fn main() {
    let matches = Command::new("MyApp")
        .version("1.0")
        .author("author <email@xxx.com>")
        .about("Does awesome things")
        .arg(arg!([name] "Optional name to operate on"))
        .arg(
            arg!(
                -c --config <FILE> "Sets a custom config file"
            )
            // 通过required表示是否可选
            .required(false)
            .value_parser(value_parser!(PathBuf)),
        )
        .arg(
            arg!(
                -d --debug "Turn debugging information on"
            )
            .action(ArgAction::Count),
        )
        .subcommand(
            Command::new("test")
                .about("does testing things")
                .arg(arg!(-l --list "lists test values").action(ArgAction::SetTrue)),
        )
        .get_matches();

    if let Some(name) = matches.get_one::<String>("name") {
        println!("Value for name: {}", name);
    }

    if let Some(config_path) = matches.get_one::<PathBuf>("config") {
        println!("Value for config: {}", config_path.display());
    }

    match matches
        .get_one::<u8>("debug")
        .expect("Count's are defaulted")
    {
        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"),
    }

    if let Some(matches) = matches.subcommand_matches("test") {
        // 运行myapp test
        if *matches.get_one::<bool>("list").expect("defaulted by clap") {
            // 运行myapp test -l时
            println!("Printing testing lists...");
        } else {
            println!("Not printing testing lists...");
        }
    }
}

更多,更详细的内容建议参考官方的文档。用法和示例都比较详细。

参考