rust clap default_values_t 表示向量,其中标志可能重复,也可能不重复

问题描述 投票:0回答:1
    #[clap(long = "type", num_args = 0.., default_values_t = [MyType::a], value_parser = MyType::from_string)]
    pub name_types: Vec<MyType>,

    #[clap(long = "name", num_args = 1..)]
    pub names: Vec<String> 

我有一个类似的代码(MyType 是一个具有两个可能值的枚举,假设“a”和“b”)。我可以调用我的 cli:

my-cli --name andrew --type a --name billy --type b
,它将创建名称为
["andrew","billy"]
和 name_types 为
[MyType::a, MyType::b]
。但我想将其称为
 my-cli --name andrew --name billy --type b
并期望它也能正常工作(类型的默认值,如果它没有预设为
MyType::a
)。另外,我希望它保持顺序,即如果我将 cli 称为
my-cli --name andrew  --name billy --type b --name carol
=>
name_types = [a,b,a]

rust clap
1个回答
0
投票

据我所知,您隐含地期望

names
name_types
的元素之间存在一对一的关系。好吧,你还没有告诉 clap 这件事,
clap
也不适合指定这种不平凡的逻辑关系 - 这不是命令行解析器的工作。

相反,最好的方法可能是让你的

clap
东西保持简单。然后将自定义逻辑写入“后处理”
clap
的输出到正确编码逻辑关系的结构中,并在核心应用程序中使用它。

这是我要写的:

use clap::{CommandFactory, FromArgMatches, Parser, ValueEnum};

// ValueEnum is probably the better way to do this
// unless your type is actually more complicated than in this example
#[derive(Debug, Copy, Clone, ValueEnum)]
enum MyType {
    A,
    B,
}

#[derive(Debug, Parser)]
struct Cli {
    // #[clap(...)] is deprecated
    #[arg(long = "type")]
    pub name_types: Vec<MyType>,

    // num_args doesn't do what you probably think it does
    // see https://github.com/clap-rs/clap/issues/4507#issuecomment-1372250231
    #[arg(long = "name", required = true)]
    pub names: Vec<String>,
}

// this type properly expresses the logical relation
// "each name must have an associated type"
#[derive(Debug)]
struct User {
    name: String,
    name_type: MyType,
}

fn main() {
    let matches = Cli::command().get_matches();
    let Cli { name_types, names } = Cli::from_arg_matches(&matches).unwrap();

    // get indices of `names`
    let name_indices = matches
        .indices_of("names")
        .into_iter()
        .flatten()
        .collect::<Vec<_>>();

    // get the applicable index range of the `--type` option for each name
    // the `--type` options found between a `--name` and the next is applicable
    let applicable_type_index_ranges = {
        let mut window_indices = name_indices.clone();
        // for the last `--name` option, the upper index is unbounded
        window_indices.push(std::usize::MAX);
        window_indices
            .windows(2)
            .map(|window| match window {
                [start, end] => (*start, *end),
                _ => unreachable!(),
            })
            .collect::<Vec<_>>()
    };
    assert_eq!(names.len(), applicable_type_index_ranges.len());

    // pair the `--type` options with their indices
    let name_types_with_indices = {
        let type_indices = matches
            .indices_of("name_types")
            .into_iter()
            .flatten()
            .collect::<Vec<_>>();
        assert_eq!(name_types.len(), type_indices.len());
        name_types.into_iter().zip(type_indices).collect::<Vec<_>>()
    };

    // construct `User`s with types properly assigned
    let users = names
        .into_iter()
        .zip(applicable_type_index_ranges)
        .map(|(name, (start_idx, end_idx))| {
            let name_type = name_types_with_indices
                .iter()
                // only `--type` options within the range are applicable
                .filter_map(|&(t, idx)| (idx > start_idx && idx < end_idx).then_some(t))
                // assuming you want the last applicable `--type` to take precedence
                .last()
                // your default value if no `--type` is present
                .unwrap_or(MyType::A);
            User { name, name_type }
        })
        .collect::<Vec<_>>();

    dbg!(users);
}
$ cargo run -- --name romeo --type b --name juliett
[src/main.rs:86:5] users = [
    User {
        name: "romeo",
        name_type: B,
    },
    User {
        name: "juliett",
        name_type: A,
    },
]

如您所见,这是可以完成的,但它并不简单,在很多地方,

clap
为您做出固执己见的设计决策会很奇怪。

© www.soinside.com 2019 - 2024. All rights reserved.