在 clap cli 中返回错误并优雅退出

The*_*erk 2 rust clap

我想实现一个在 git 存储库上运行的简单 clap cli,但这对问题并不重要;我相信这将有助于澄清。我试图确定如果不从存储库的根运行的话,退出时出现错误的最惯用的方法。这里有三个选项;我不确定是否有好的。

执行这些步骤的最佳方法是什么:

  1. 检查我是否从 repo root 运行
  2. 如果是则继续,如果不是则退出
  3. 如果没有给出命令,则生成帮助
  4. 如果给出命令,则运行命令

理想情况下,我能够输出错误和用法。此外,子命令中还会发生其他错误,我不确定在这些情况下优雅退出的最佳方法。

考虑以下 cli 定义:

use clap::ErrorKind::Io;
use clap::{Parser, Subcommand};
use git2::Repository;
use std::process;

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

#[derive(Debug, Subcommand)]
enum Commands {
    /// Do a thing.
    Do,
}
Run Code Online (Sandbox Code Playgroud)

我目前看到的三个主要选项是:

选项1

fn main() -> Result<(), String> {
    let repo = match Repository::open(".") {
        Ok(repo) => repo,
        Err(_) => return Err("must be run from root of repository".to_owned()),
    };
    let args = Cli::parse();
    match args.command {
        Commands::Do => {
            println!("{:?}: Doing a thing with the repository.", repo.workdir());
        }
    }
    Ok(())
}
Run Code Online (Sandbox Code Playgroud)

选项2

fn main() {
    let repo = match Repository::open(".") {
        Ok(repo) => repo,
        Err(_) => {
            eprintln!("{}", "must be run from root of repository".to_owned());
            process::exit(1);
        }
    };
    let args = Cli::parse();
    match args.command {
        Commands::Do => {
            println!("{:?}: Doing a thing with the repository.", repo.workdir());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

选项3

fn main() -> clap::Result<(), clap::Error> {
    let repo = match Repository::open(".") {
        Ok(repo) => repo,
        Err(_) => return Err(clap::Error::raw(Io, "not in repo")),
    };
    let args = Cli::parse();
    match args.command {
        Commands::Do => {
            println!("{:?}: Doing a thing with the repository.", repo.workdir());
        }
    }
    Ok(())
}
Run Code Online (Sandbox Code Playgroud)

这些中的任何一个或全部是可怕的、有用的还是可以改进的?


我看到了寻求主观信息的结束投票,但我所追求的可能比看起来更加二元。我当然会尊重社区的意愿,但我想知道是否有任何或所有这些都严重不符合规范,或者由于某种原因存在问题。

Fin*_*nis 5

我个人非常喜欢错误包装库,例如eyremiette

一些备注:

  • 考虑使用.map_err()而不是match将错误转换为不同的错误,它更紧凑且更易于阅读
  • 考虑使用?运算符而不是return向上传播错误。该?运算符是“如果 Ok 则展开,如果 Err 则返回错误”的简短版本,使代码更易于阅读。

这是一个使用的示例anyhow

fn main() -> anyhow::Result<()> {
    let repo = Repository::open(".").map_err(|_| anyhow!("must be run from root of repository"))?;
    let args = Cli::parse();
    match args.command {
        Commands::Do => {
            println!("{:?}: Doing a thing with the repository.", repo.workdir());
        }
    }
    Ok(())
}
Run Code Online (Sandbox Code Playgroud)