clap

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Clap (Rust) - Production CLI Patterns

Clap (Rust) - 生产级CLI模式

Overview

概述

Clap provides declarative command-line parsing with strong help output, validation, and subcommand support. Use it to build CLIs with predictable UX and testable execution paths.
Clap提供声明式的命令行解析功能,附带完善的帮助输出、验证机制和子命令支持。使用它可以构建具有可预测用户体验和可测试执行路径的CLI。

Quick Start

快速开始

Minimal CLI

最简CLI

Correct: derive Parser
rust
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);
}
Wrong: parse multiple times
rust
fn main() {
    let _a = Args::parse();
    let _b = Args::parse(); // duplicate parsing and inconsistent behavior
}
正确做法:派生Parser
rust
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);
}
错误做法:多次解析
rust
fn main() {
    let _a = Args::parse();
    let _b = Args::parse(); // duplicate parsing and inconsistent behavior
}

Subcommands (real tools)

子命令(实战工具)

Model multi-mode CLIs with subcommands and shared global flags.
Correct: global flags + subcommands
rust
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。
正确做法:全局标志 + 子命令
rust
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),
    }
}

Config layering (CLI + env + config file)

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

Prefer explicit precedence:
  1. CLI flags
  2. Environment variables
  3. Config file
  4. Defaults
Correct: merge config with CLI overrides
rust
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)
}
遵循明确的优先级顺序:
  1. CLI标志
  2. 环境变量
  3. 配置文件
  4. 默认值
正确做法:合并配置并支持CLI覆盖
rust
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)
}

Exit codes and error handling

退出码与错误处理

Map failures to stable exit codes. Return
Result
from command handlers and centralize printing.
Correct: command returns Result
rust
use std::process::ExitCode;

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

fn run() -> Result<(), String> {
    Ok(())
}
将故障映射为稳定的退出码。从命令处理函数返回
Result
并集中处理打印逻辑。
正确做法:命令返回Result
rust
use std::process::ExitCode;

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

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

Testing (assert_cmd)

测试(assert_cmd)

Test the binary surface (arguments, output, exit codes) without coupling to internals.
Correct: integration test
rust
use assert_cmd::Command;

#[test]
fn shows_help() {
    Command::cargo_bin("mytool")
        .unwrap()
        .arg("--help")
        .assert()
        .success();
}
在不耦合内部实现的前提下,测试二进制程序的外部接口(参数、输出、退出码)。
正确做法:集成测试
rust
use assert_cmd::Command;

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

Shell completions (optional)

Shell补全(可选)

Generate completions for Bash/Zsh/Fish.
Correct: emit completions
rust
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());
}
为Bash/Zsh/Fish生成补全脚本。
正确做法:生成补全
rust
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());
}

Anti-Patterns

反模式

  • Parse arguments in library code; parse once in
    main
    and pass a typed config down.
  • Hide failures behind
    unwrap
    ; return stable exit codes and structured errors.
  • Overload one command with flags; use subcommands for distinct modes.
  • 在库代码中解析参数;应在
    main
    中仅解析一次,然后向下传递类型化的配置。
  • 使用
    unwrap
    隐藏故障;应返回稳定的退出码和结构化错误。
  • 用标志重载单个命令;应为不同模式使用子命令。

Resources

资源