mcpField
C
clap

clap

github.com/bobmatnyc开发78335 次安装15 周前更新
代码仓库

关于 clap

面向 Rust CLI 开发者的 Clap 模式指南,介绍声明式参数解析、子命令、配置优先级、错误处理与测试。还涵盖 Shell 补全生成。

主要功能

  1. 01声明式参数解析与帮助输出
  2. 02组织子命令和全局标志
  3. 03合并命令行、环境变量与配置
  4. 04统一错误处理和退出码
  5. 05测试二进制接口
  6. 06生成 Shell 补全

适用场景

  1. 01构建带多个子命令的 Rust CLI
  2. 02为配置来源设定明确优先级
  3. 03验证参数、输出和退出码

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill clap

安装后在 Claude Code 中输入/clap确认已加载。

附带文件metadata.json

详细介绍

Clap(Rust)——生产级 CLI 模式

概述

Clap 提供声明式命令行解析,并支持完善的帮助输出、验证和子命令。使用它构建体验一致、执行路径可测试的 CLI。

快速入门

最简 CLI

✅ 正确:派生 Parser

use clap::Parser;

#[derive(Parser, Debug)]
#[command(name = "mytool", version, about = "Example CLI")]
struct Args {
    /// Enable verbose output
    #[arg(long)]
    verbose: bool,

    /// Input file path
    #[arg(value_name = "FILE")]
    input: String,
}

fn main() {
    let args = Args::parse();
    if args.verbose {
        eprintln!("verbose enabled");
    }
    println!("input={}", args.input);
}

❌ 错误:多次解析

fn main() {
    let _a = Args::parse();
    let _b = Args::parse(); // duplicate parsing and inconsistent behavior
}

子命令(真实工具)

使用子命令和共享的全局标志,为多模式 CLI 建模。

✅ 正确:全局标志 + 子命令

use clap::{Parser, Subcommand, ValueEnum};

#[derive(Parser, Debug)]
struct Args {
    #[arg(long, global = true)]
    verbose: bool,

    #[arg(long, global = true, env = "MYTOOL_CONFIG")]
    config: Option<String>,

    #[command(subcommand)]
    cmd: Command,
}

#[derive(Subcommand, Debug)]
enum Command {
    Serve { #[arg(long, default_value_t = 3000)] port: u16 },
    Migrate { #[arg(long, value_enum, default_value_t = Mode::Up)] mode: Mode },
}

#[derive(Copy, Clone, Debug, ValueEnum)]
enum Mode { Up, Down }

fn main() {
    let args = Args::parse();
    match args.cmd {
        Command::Serve { port } => println!("serve on {}", port),
        Command::Migrate { mode } => println!("migrate: {:?}", mode),
    }
}

配置分层(CLI + 环境变量 + 配置文件)

优先级应明确:

  1. CLI 标志
  2. 环境变量
  3. 配置文件
  4. 默认值

✅ 正确:合并配置并应用 CLI 覆盖项

use clap::Parser;
use serde::Deserialize;

#[derive(Parser, Debug)]
struct Args {
    #[arg(long, env = "APP_PORT")]
    port: Option<u16>,
}

#[derive(Deserialize)]
struct FileConfig {
    port: Option<u16>,
}

fn effective_port(args: &Args, file: &FileConfig) -> u16 {
    args.port.or(file.port).unwrap_or(3000)
}

退出代码和错误处理

将失败映射为稳定的退出代码。让命令处理函数返回 Result,并集中处理打印。

✅ 正确:命令返回 Result

use std::process::ExitCode;

fn main() -> ExitCode {
    match run() {
        Ok(()) => ExitCode::SUCCESS,
        Err(e) => {
            eprintln!("{e}");
            ExitCode::from(1)
        }
    }
}

fn run() -> Result<(), String> {
    Ok(())
}

测试(assert_cmd)

测试二进制程序的外部接口(参数、输出、退出代码),避免与内部实现耦合。

✅ 正确:集成测试

use assert_cmd::Command;

#[test]
fn shows_help() {
    Command::cargo_bin("mytool")
        .unwrap()
        .arg("--help")
        .assert()
        .success();
}

Shell 补全(可选)

为 Bash/Zsh/Fish 生成补全。

✅ 正确:生成补全

use clap::{CommandFactory, Parser};
use clap_complete::{generate, shells::Zsh};
use std::io;

fn print_zsh_completions() {
    let mut cmd = super::Args::command();
    generate(Zsh, &mut cmd, "mytool", &mut io::stdout());
}

反模式

  • 在库代码中解析参数;应在 main 中解析一次,并向下传递类型化配置。
  • 用 unwrap 隐藏失败;应返回稳定的退出代码和结构化错误。
  • 在一个命令中堆叠过多标志;对于不同模式,应使用子命令。

资源

评论

支持 Markdown