Идиоматический способ ржавчины правильно разобрать Clap ArgMatches - PullRequest
1 голос
/ 14 октября 2019

Я изучаю ржавчину и пытаюсь найти утилиту типа find (да еще одну), я использую clap и пытаюсь поддерживать командную строку и файл конфигурации для параметров программы (это не имеет ничего общего с файлом clap yml).

Я пытаюсь проанализировать команды, и если в приложение не было передано никаких команд, я попытаюсь загрузить их из файла конфигурации. Теперь я не знаю, как это сделать идиоматическим образом.

fn main() {
    let matches = App::new("findx")
        .version(crate_version!())
        .author(crate_authors!())
        .about("find + directory operations utility")
        .arg(
            Arg::with_name("paths")
               ...
        )
        .arg(
            Arg::with_name("patterns")
              ...
        )
        .arg(
            Arg::with_name("operation")
            ...
        )
        .get_matches();
    let paths;
    let patterns;
    let operation;
    if matches.is_present("patterns") && matches.is_present("operation") {
        patterns = matches.values_of("patterns").unwrap().collect();
        paths = matches.values_of("paths").unwrap_or(clap::Values<&str>{"./"}).collect(); // this doesn't work
        operation = match matches.value_of("operation").unwrap() { // I dont like this
            "Append" => Operation::Append,
            "Prepend" => Operation::Prepend,
            "Rename" => Operation::Rename,
            _ => {
                print!("Operation unsupported");
                process::exit(1);
            }
        };
    }else if Path::new("findx.yml").is_file(){
        //TODO: try load from config file
    }else{
        eprintln!("Command line parameters or findx.yml file must be provided");
        process::exit(1);
    }


    if let Err(e) = findx::run(Config {
        paths: paths,
        patterns: patterns,
        operation: operation,
    }) {
        eprintln!("Application error: {}", e);
        process::exit(1);
    }
}

Существует идиоматический способ извлечения значений типов Option и Result в одну и ту же область, я имею в виду все примеры, которыеЯ прочитал, использую match или if let Some(x), чтобы использовать значение x в области сопоставления с образцом, но мне нужно присвоить значение переменной.

Может кто-нибудь помочь мне с этимили указываете мне правильное направление?

С наилучшими пожеланиями

1 Ответ

1 голос
/ 15 октября 2019

Лично я не вижу ничего плохого в использовании операторов сопоставления и сворачивании их или размещении в другой функции. Но если вы хотите удалить его, есть много опций.

Существует возможность использовать метод .default_value_if(), который равен impl для clap::Arg и имеет другое значение по умолчанию, в зависимости от того, какое плечо соответствуетmatched.

Из документации хлоп

//sets  value of arg "other" to "default" if value of "--opt" is "special"

let m = App::new("prog")
    .arg(Arg::with_name("opt")
        .takes_value(true)
        .long("opt"))
    .arg(Arg::with_name("other")
        .long("other")
        .default_value_if("opt", Some("special"), "default"))
    .get_matches_from(vec![
        "prog", "--opt", "special"
    ]);

assert_eq!(m.value_of("other"), Some("default"));

Кроме того, вы можете добавить валидатор в operation ИЛИ преобразовать действительные значения операции в флаги.

Ниже приведен пример преобразования значений вашей спичечной руки в отдельные флаги (меньший пример для ясности).

extern crate clap;

use clap::{Arg,App};

fn command_line_interface<'a>() -> clap::ArgMatches<'a> {
    //Sets the command line interface of the program.
    App::new("something")
            .version("0.1")
            .arg(Arg::with_name("rename")
                 .help("renames something")
                 .short("r")
                 .long("rename"))
            .arg(Arg::with_name("prepend")
                 .help("prepends something")
                 .short("p")
                 .long("prepend"))
            .arg(Arg::with_name("append")
                 .help("appends something")
                 .short("a")
                 .long("append"))
            .get_matches()
}

#[derive(Debug)]
enum Operation {
    Rename,
    Append,
    Prepend,
}


fn main() {
    let matches  = command_line_interface();

    let operation = if matches.is_present("rename") {
        Operation::Rename
    } else if matches.is_present("prepend"){
        Operation::Prepend
    } else {
        //DEFAULT
        Operation::Append
    };
    println!("Value of operation is {:?}",operation);
}

Надеюсь, это поможет!

РЕДАКТИРОВАТЬ:

Вы также можете использовать Подкоманды с вашими конкретными операциями. Все зависит от того, каким интерфейсом вы хотите быть.

 let app_m = App::new("git")
     .subcommand(SubCommand::with_name("clone"))
     .subcommand(SubCommand::with_name("push"))
     .subcommand(SubCommand::with_name("commit"))
     .get_matches();

match app_m.subcommand() {
    ("clone",  Some(sub_m)) => {}, // clone was used
    ("push",   Some(sub_m)) => {}, // push was used
    ("commit", Some(sub_m)) => {}, // commit was used
    _                       => {}, // Either no subcommand or one not tested for...
}
...