domain-cli

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

CLI Domain

CLI 领域

Layer 3: Domain Constraints
第3层:领域约束

Domain Constraints → Design Implications

领域约束 → 设计影响

Domain RuleDesign ConstraintRust Implication
User ergonomicsClear help, errorsclap derive macros
Config precedenceCLI > env > fileLayered config loading
Exit codesNon-zero on errorProper Result handling
Stdout/stderrData vs errorseprintln! for errors
InterruptibleHandle Ctrl+CSignal handling

领域规则设计约束Rust 实现建议
用户易用性清晰的帮助信息与错误提示clap derive macros
配置优先级CLI 参数 > 环境变量 > 配置文件分层配置加载
退出码出错时返回非零值正确的 Result 处理
标准输出/标准错误数据输出到stdout,错误输出到stderr使用eprintln!输出错误
可中断性处理Ctrl+C信号处理

Critical Constraints

关键约束

User Communication

用户通信

RULE: Errors to stderr, data to stdout
WHY: Pipeable output, scriptability
RUST: eprintln! for errors, println! for data
RULE: Errors to stderr, data to stdout
WHY: Pipeable output, scriptability
RUST: eprintln! for errors, println! for data

Configuration Priority

配置优先级

RULE: CLI args > env vars > config file > defaults
WHY: User expectation, override capability
RUST: Layered config with clap + figment/config
RULE: CLI args > env vars > config file > defaults
WHY: User expectation, override capability
RUST: Layered config with clap + figment/config

Exit Codes

退出码

RULE: Return non-zero on any error
WHY: Script integration, automation
RUST: main() -> Result<(), Error> or explicit exit()

RULE: Return non-zero on any error
WHY: Script integration, automation
RUST: main() -> Result<(), Error> or explicit exit()

Trace Down ↓

向下追溯 ↓

From constraints to design (Layer 2):
"Need argument parsing"
    ↓ m05-type-driven: Derive structs for args
    ↓ clap: #[derive(Parser)]

"Need config layering"
    ↓ m09-domain: Config as domain object
    ↓ figment/config: Layer sources

"Need progress display"
    ↓ m12-lifecycle: Progress bar as RAII
    ↓ indicatif: ProgressBar

从约束到设计(第2层):
"Need argument parsing"
    ↓ m05-type-driven: Derive structs for args
    ↓ clap: #[derive(Parser)]

"Need config layering"
    ↓ m09-domain: Config as domain object
    ↓ figment/config: Layer sources

"Need progress display"
    ↓ m12-lifecycle: Progress bar as RAII
    ↓ indicatif: ProgressBar

Key Crates

关键依赖库(Crates)

PurposeCrate
Argument parsingclap
Interactive promptsdialoguer
Progress barsindicatif
Colored outputcolored
Terminal UIratatui
Terminal controlcrossterm
Console utilitiesconsole
用途Crate
参数解析clap
交互式提示dialoguer
进度条indicatif
彩色输出colored
终端UIratatui
终端控制crossterm
控制台工具console

Design Patterns

设计模式

PatternPurposeImplementation
Args structType-safe args
#[derive(Parser)]
SubcommandsCommand hierarchy
#[derive(Subcommand)]
Config layersOverride precedenceCLI > env > file
ProgressUser feedback
ProgressBar::new(len)
模式用途实现方式
参数结构体类型安全的参数处理
#[derive(Parser)]
子命令命令层级结构
#[derive(Subcommand)]
配置分层覆盖优先级CLI > 环境变量 > 配置文件
进度显示用户反馈
ProgressBar::new(len)

Code Pattern: CLI Structure

代码模式:CLI 结构

rust
use clap::{Parser, Subcommand};

#[derive(Parser)]
#[command(name = "myapp", about = "My CLI tool")]
struct Cli {
    /// Enable verbose output
    #[arg(short, long)]
    verbose: bool,

    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// Initialize a new project
    Init { name: String },
    /// Run the application
    Run {
        #[arg(short, long)]
        port: Option<u16>,
    },
}

fn main() -> anyhow::Result<()> {
    let cli = Cli::parse();
    match cli.command {
        Commands::Init { name } => init_project(&name)?,
        Commands::Run { port } => run_server(port.unwrap_or(8080))?,
    }
    Ok(())
}

rust
use clap::{Parser, Subcommand};

#[derive(Parser)]
#[command(name = "myapp", about = "My CLI tool")]
struct Cli {
    /// Enable verbose output
    #[arg(short, long)]
    verbose: bool,

    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// Initialize a new project
    Init { name: String },
    /// Run the application
    Run {
        #[arg(short, long)]
        port: Option<u16>,
    },
}

fn main() -> anyhow::Result<()> {
    let cli = Cli::parse();
    match cli.command {
        Commands::Init { name } => init_project(&name)?,
        Commands::Run { port } => run_server(port.unwrap_or(8080))?,
    }
    Ok(())
}

Common Mistakes

常见错误

MistakeDomain ViolationFix
Errors to stdoutBreaks pipingeprintln!
No help textPoor UX#[arg(help = "...")]
Panic on errorBad exit codeResult + proper handling
No progress for long opsUser uncertaintyindicatif

错误违反的领域规则修复方案
错误输出到stdout破坏管道功能使用eprintln!
无帮助文本糟糕的用户体验添加#[arg(help = "...")]
出错时触发Panic错误的退出码使用Result+正确的错误处理
长时间操作无进度提示用户不确定状态使用indicatif

Trace to Layer 1

追溯到第1层

ConstraintLayer 2 PatternLayer 1 Implementation
Type-safe argsDerive macrosclap Parser
Error handlingResult propagationanyhow + exit codes
User feedbackProgress RAIIindicatif ProgressBar
Config precedenceBuilder patternLayered sources

约束第2层模式第1层实现
类型安全的参数派生宏clap Parser
错误处理Result传播anyhow + 退出码
用户反馈进度条RAII模式indicatif ProgressBar
配置优先级构建器模式分层数据源

Related Skills

相关技能

WhenSee
Error handlingm06-error-handling
Type-driven argsm05-type-driven
Progress lifecyclem12-lifecycle
Async CLIm07-concurrency
场景参考内容
错误处理m06-error-handling
类型驱动的参数m05-type-driven
进度条生命周期m12-lifecycle
异步CLIm07-concurrency