【问题标题】:How to use an enum that represents subcommands with StructOpt?如何使用表示带有 StructOpt 的子命令的枚举?
【发布时间】:2018-11-13 09:00:00
【问题描述】:

参考"Git" example of StructOpt,我不明白我应该如何使用参数中的数据。

我对 Rust 还很陌生,所以我猜这很明显。不幸的是,我可以找到的所有带有枚举的示例都只在对象上执行println!,所以我被卡住了。我想我会做一个match,但它不起作用。

然后您将如何找到用户传递了哪些命令来运行您的程序?

#[macro_use]
extern crate structopt;

use std::path::PathBuf;
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
#[structopt(name = "git", about = "the stupid content tracker")]
enum Git {
    #[structopt(name = "add")]
    Add {
        #[structopt(short = "i")]
        interactive: bool,
        #[structopt(short = "p")]
        patch: bool,
        #[structopt(parse(from_os_str))]
        files: Vec<PathBuf>
    },
    #[structopt(name = "fetch")]
    Fetch {
        #[structopt(long = "dry-run")]
        dry_run: bool,
        #[structopt(long = "all")]
        all: bool,
        repository: Option<String>
    },
    #[structopt(name = "commit")]
    Commit {
        #[structopt(short = "m")]
        message: Option<String>,
        #[structopt(short = "a")]
        all: bool
    }
}

fn main() {
    let opt = Git::from_args();
    println!("{:?}", opt);

    match opt() {
        Git::Add(cmd) => println!("{:?}", cmd.interactive),
        _ => (),
    }
}

编译:

05:42 $ cargo run -- add -i
   Compiling example v0.1.0 (file:///Users/froyer/src/example)
error[E0532]: expected tuple struct/variant, found struct variant `Git::Add`
  --> src/main.rs:41:9
   |
41 |         Git::Add(cmd) => println!("{:?}", cmd.interactive),
   |         ^^^^^^^^ did you mean `Git::Add { /* fields */ }`?

error[E0618]: expected function, found enum variant `opt`
  --> src/main.rs:40:11
   |
37 |     let opt = Git::from_args();
   |         --- `opt` defined here
...
40 |     match opt() {
   |           ^^^^^ not a function
help: `opt` is a unit variant, you need to write it without the parenthesis
   |
40 |     match opt {
   |           ^^^

【问题讨论】:

    标签: rust enums arguments structopt


    【解决方案1】:

    感谢issue #1 in the structopt repository,我终于明白它应该如何工作了:)

    fn main () {
        match Git::from_args() {
            Git::Add { interactive, patch, files } => {
                println!("{:?}", interactive)
            },
            Git::Commit { message, all } => {
                //...
            }
            _ => (),
        }
    }
    

    【讨论】:

    • 似乎这种将枚举用于子命令的模式对于代码的可维护性会很烦人。任何你想在子命令中使用参数的地方都会有新参数的维护负担。试图弄清楚是否有某种方法可以使用结构......
    • 没关系,我想我找到了一种模式,它通过为 enum 子命令(另一个 StructOpt 结构)设置一个参数来工作。
    【解决方案2】:

    我遇到了同样的问题,并认为我会进一步清除 @kellpossible 的示例:

    #[macro_use]
    extern crate structopt;
    
    pub use structopt::StructOpt;
    use std::path::PathBuf;
    
    
    
    #[derive(Debug, StructOpt)]
    #[structopt(name = "example", about="how to use struct-opt crate")]
    pub struct Opts{
    
        #[structopt(short = "v",  parse(from_occurrences))]
        verbosity: u8,
    
        // SUBCOMMANDS
        #[structopt(subcommand)]
        commands: Option<Git>
    
    }
    
    
    #[derive(StructOpt, Debug)]
    #[structopt(name = "git", about = "the stupid content tracker")]
    enum Git {
        #[structopt(name = "add")]
        Add (AddOpts),
    
        #[structopt(name = "fetch")]
        Fetch(FetchOpts),
    
        #[structopt(name = "commit")]
        Commit(CommitOpts)
    }
    
    #[derive(StructOpt, Debug)]
    struct AddOpts {
        #[structopt(short = "i")]    
        interactive: bool,
    
        #[structopt(short = "p")]
        patch: bool,
    
        #[structopt(parse(from_os_str))]
        files: Vec<PathBuf>
    }
    
    #[derive(Debug, StructOpt)]
    pub struct FetchOpts {
        #[structopt(long = "dry-run")]
        dry_run: bool,
        #[structopt(long = "all")]
        all: bool,
        repository: Option<String>
    }
    
    #[derive(Debug, StructOpt)]
    pub struct CommitOpts {
        #[structopt(short = "m")]
        message: Option<String>,
        #[structopt(short = "a")]
        all: bool
    }
    
    
    fn main() {
        println!("Hello subcommands!");    
    
        let opt = Opts::from_args();
        handle_subcommand(opt);
    
    }
    
    fn handle_subcommand(opt: Opts){
        // handle subcommands
        if let Some(subcommand) = opt.commands{
            match subcommand {
                Git::Add(cfg) => {
                    println!("handle Add:  {:?}", cfg);
                },
                Git::Commit(cfg) => {
                    println!("handle Commit: {:?}", cfg);
                },
                Git::Fetch(cfg) => {
                    println!("handle Fetch: {:?}", cfg);
                },
    
            }
        }
    }
    

    希望这会有所帮助,但如果有人知道更好的方法来做这件事会很感兴趣。

    【讨论】:

      猜你喜欢
      • 2019-07-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-01
      • 2013-01-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多