rust 如何使用Clap derive解析自定义字符串

1l5u6lss  于 2022-11-24  发布在  其他
关注(0)|答案(1)|浏览(202)

我找到了我想要的链接:Parse user input String with clap for command line programming但这并不完全清楚。我看到很多使用App::new()的帖子,但我在clap文档中找不到任何痕迹。
我想在我的rust应用程序中解析一个字符串(它不是来自cmdline)目前我是这样做的:

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
    /// Optional name to operate on
    test: String,
    name: Option<String>,
}

pub fn test(mut to_parse: &String) {
    let Ok(r) = shellwords::split(to_parse) else {
        info!("error during process");
        return;
    };
    let test = Cli::parse_from(r);
    if let Some(name) = test.name.as_deref() {
        println!("Value for name: {}", name);
    }
}

它是可以工作的,但是也许我对clap的工作原理还不够了解。它只接受位置参数,比如--test或者--name。一定有办法在参数之前有一个命令吧?我还有一个问题:如果我使用struct定义一个命令,并使用Cli::parse_from()解析字符串,那么如何使用多个命令解析字符串(可能不清楚,但是如何使用多个命令?)
------EDIT我玩过一点clap,现在我有这样的东西:

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
    #[command(subcommand)]
    command: Com,
}

/// Doc comment
#[derive(Subcommand)]
enum Com {
    /// First command
    Test(TestArg),
    ///Second command
    SecondTest(OtherArg),
}
#[derive(Args)]
struct TestArg {
    name: String,
}

#[derive(Args)]
struct OtherArg {
    name: Option<String>,
    and: Option<String>,
}

然而,当我在游戏控制台中输入命令时(仍然使用parse_from解析),没有任何东西被识别,我总是收到错误信息。

y1aodyip

y1aodyip1#

你的问题没有显示你传递了什么,你得到了什么错误,所以很难指出问题。
我认为您可能遗漏了clap(显然)在解析字符串切片数组时所需的第一个参数,即二进制名称/路径。
使用上面的代码(第二个代码段),下面的代码段无法工作。

let cli = Cli::parse_from(["test", "asd"]);
dbg!(cli); // You need to add #[derive(Debug)] to your structs for it to work

因为它以类似于

error: The subcommand 'asd' wasn't recognized

Usage: test <COMMAND>

For more information try '--help'

如果仔细观察,您会发现test参数(我们试图将其作为子命令传递给Cli结构)被识别为二进制名称。
如果我们在开始处添加一个额外的参数(内容无关紧要,甚至可以是空字符串切片),它就可以工作。

fn main() {
    let cli = Cli::parse_from(["", "test", "asd"]);
    dbg!(cli);
}

上面的代码段打印成功解析的参数

[src/main.rs:32] cli = Cli {
    command: Test(
        TestArg {
            name: "asd",
        },
    ),
}

它也可以与第二个子命令SecondTest一起使用

let cli = Cli::parse_from(["", "second-test", "some name"]);
dbg!(cli);

打印

[src/main.rs:35] cli = Cli {
    command: SecondTest(
        OtherArg {
            name: Some(
                "some name",
            ),
            and: None,
        },
    ),
}

尽管如此,看起来你的代码缺少了一些属性宏,以便按照(我认为)你期望的那样工作。
假设您要从字符串(如

mycmd test --name someName

TestArg结构应该如下所示

#[derive(Args)]
struct TestArg {
    #[clap(short, long)]
    name: String,
}

现在,name不是作为位置参数,而是作为标志(?)参数(在本例中是必需的),其中参数需要作为--name <NAME>-n <NAME>提供
对于第二个子命令也是一样,可以如下声明

#[derive(Args, Debug)]
struct OtherArg {
    #[clap(long)]
    name: Option<String>,
    #[clap(long)]
    and: Option<String>,
}

然后,您需要传递带有长名称标志的参数,如下所示:

let cli = Cli::parse_from([
    "",
    "second-test",
    "--name",
    "some name",
    "--and",
    "other name",
]);
dbg!(cli);

这导致了

[src/main.rs:41] cli = Cli {
    command: SecondTest(
        OtherArg {
            name: Some(
                "some name",
            ),
            and: Some(
                "other name",
            ),
        },
    ),
}

相关问题