clap
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseClap (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:
- CLI flags
- Environment variables
- Config file
- 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)
}遵循明确的优先级顺序:
- CLI标志
- 环境变量
- 配置文件
- 默认值
✅ 正确做法:合并配置并支持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 from command handlers and centralize printing.
Result✅ 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 and pass a typed config down.
main - Hide failures behind ; return stable exit codes and structured errors.
unwrap - Overload one command with flags; use subcommands for distinct modes.
- 在库代码中解析参数;应在中仅解析一次,然后向下传递类型化的配置。
main - 使用隐藏故障;应返回稳定的退出码和结构化错误。
unwrap - 用标志重载单个命令;应为不同模式使用子命令。
Resources
资源
- Clap: https://docs.rs/clap
- assert_cmd: https://docs.rs/assert_cmd
- clap_complete: https://docs.rs/clap_complete
- Clap: https://docs.rs/clap
- assert_cmd: https://docs.rs/assert_cmd
- clap_complete: https://docs.rs/clap_complete