簡體   English   中英

在 rust 中處理錯誤的最佳方法是什么? (帶有拍手的 CLI)

[英]What is the best way of handling error in rust? (CLI with clap)

我正在嘗試使用 clap 在 rust 中開發一個簡單的 cli 賬單管理器。 我想讓用戶通過輸入其名稱和價值來添加新賬單,刪除一些賬單,我還想添加撤消和重做功能。 因為這是我在 rust 的第一個項目,所以我把它命名為 p1。 在終端中,用戶必須這樣做:

p1 -n bill1 -v 50

然后,該法案將被添加或

p1 undo 5

然后,最后 5 個操作將被撤消。 但是,由於 clap 的正常功能,至少據我所知,這種行為也被接受:

p1 -n bill2 -v 30 redo 30

我不想允許它。 我不想讓用戶同時使用標志和子命令。 所以我做了一些驗證。 為了讓您更容易幫助我,我將展示代碼的相關部分。

use clap::{Parser, Subcommand};
use std::{collections::HashMap, path::PathBuf};
use home::home_dir;
use std::fs;


#[derive(Parser, Debug)]
struct Args {
    /// The name of the bill
    #[clap(short, long, value_parser)]
    name: Option<String>,
    /// The value of the bill
    #[clap(short, long, value_parser)]
    value: Option<u32>,
    #[clap(subcommand)]
    command: Option<Commands>,
}


#[derive(Subcommand, Debug)]
enum Commands {
    /// Undo
    Undo { undo: Option<u32> },
    /// Redo
    Redo { redo: Option<u32> },
    /// Remove
    Remove { remove: Option<String> },
}

fn validate_args(args: Args) -> Result<Args, String> {
    match (&args.name, &args.value, &args.command) {
        (Some(_), Some(_), None) => Ok(args),
        (None, None, Some(_)) => Ok(args),
        (None, None, None) => Ok(args),
        _ => Err("You can't use options and subcommands at the same time".to_string())
    }
}


fn exit_on_error(error: &Result<Args, String>) {
    println!("{:?}", error);
    panic!("aaaaaaaaaaaaaaaaaa");
}


fn main() {
    let args: Result<Args, String> = validate_args(Args::parse());
    match args {
        Ok(_) => (),
        Err(_) => exit_on_error(&args)
    };
    ...
}

我需要幫助的另一件事是。 當用戶既不插入標志也不插入子命令時,只需在終端中輸入“p1”,我想將他重定向到幫助子命令,就好像他輸入了

p1 help

我該怎么做?

而且,我仍然不習慣 rust 風格的可變擁有手柄。 “exit_on_error” function 只能接收借來的結果,因為顯然字符串無法實現 Copy。 這可以防止我在打印之前解開 Err,這使得它在終端中顯示為帶引號。 我應該怎么做才能解決它?

請幫助我,如果我的問題有什么不清楚的地方,請告訴我。

我同意@SirDarius 的觀點,您可能不應該這樣做,但是,嗯,並不意味着知道如何做到這一點會很痛苦:

當用戶既不插入標志也不插入子命令時,只需在終端中輸入“p1”,我想將他重定向到幫助子命令,就好像他輸入了p1 help

如果很難知道是否從解析的Args中傳遞了任何 arguments,例如,您可以回避clap並檢查來自std::env::args()的參數計數

if std::env::args().count() <= 1 {
    Args::parse_from(&[
        // Argument 0 is always the program name.
        // Could just use "p1" here, but this is more generic:
        std::env::args()
            .next()
            .as_ref()
            .map(String::as_str)
            .unwrap_or(env!("CARGO_CRATE_NAME")),
        // as if help was typed:
        "help",
    ]);
    unreachable!("Should print help and exit");
}

我認為您也可以使用 clap 的ArgGroup來實現這種行為,但我發現它們很笨重。

而且,我仍然不習慣 rust 風格的可變擁有手柄。

這是一個經典的 Rust 問題,您可以做很多事情:

  1. 直接退出validate_args即可。 這也是Args::parse所做的。
  2. 使用let args = match args { Ok(args) => args, Err(e) => exit_on_error(e) }; (必須為此更改exit_on_error的類型。)
  3. 使exit_on_error具有類型fn(Result<Args, String>) -> Args並將match移動到其中。
  4. main return Result<(), Box<dyn std::error::Error>>這樣你就可以使用? 解開Result s(盡管您需要選擇具有良好Display實現類型而不是String的不同錯誤類型。不妨使用anyhow板條箱。)
  5. 使用error.as_ref().err().unwrap() (您甚至可以添加Option::cloned以獲得錯誤字符串的自有副本。)

最后:請在 StackOverflow 上對每個問題提出一個問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM