domain-cli について
domain-cli は、Rust のコマンドラインツール設計を扱うスキルです。出力、設定の優先順位、エラー処理、中断への対応と、関連するクレートや設計パターンを説明します。
主な機能
- 01clap 派生マクロによる引数解析
- 02設定ソースの優先順位付け
- 03データは stdout、エラーは stderr
- 04エラー時の非ゼロ終了コード
- 05Ctrl+C シグナルへの対応
- 06ターミナルのプロンプトと進捗表示
活用シーン
- 01型安全なコマンド階層の設計
- 02CLI・環境変数・ファイル間の設定上書き
- 03スクリプトでパイプ可能な出力の維持
- 04長時間処理の進捗表示
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ actionbook/ rust-skills --skill domain-cliインストール後、Claude Code で/domain-cliを入力して読み込まれたか確認します。
詳細
CLI ドメイン
レイヤー 3:ドメイン制約
ドメイン制約 → 設計への影響
| ドメインルール | 設計上の制約 | Rust での実装 |
|---|---|---|
| ユーザーの使いやすさ | 分かりやすいヘルプ、エラー | clap の derive マクロ |
| 設定の優先順位 | CLI > env > file | 階層化された設定の読み込み |
| 終了コード | エラー時は 0 以外 | 適切な Result の処理 |
| 標準出力/標準エラー | データとエラーの分離 | エラーには eprintln! |
| 割り込み可能 | Ctrl+C の処理 | シグナル処理 |
重要な制約
ユーザーへの伝達
RULE: Errors to stderr, data to stdout
WHY: Pipeable output, scriptability
RUST: eprintln! for errors, println! for data
設定の優先順位
RULE: CLI args > env vars > config file > defaults
WHY: User expectation, override capability
RUST: Layered config with clap + figment/config
終了コード
RULE: Return non-zero on any error
WHY: Script integration, automation
RUST: main() -> Result<(), Error> or explicit exit()
設計をたどる ↓
制約から設計へ(レイヤー 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
主なクレート
| 用途 | クレート |
|---|---|
| 引数の解析 | clap |
| 対話型プロンプト | dialoguer |
| プログレスバー | indicatif |
| 色付き出力 | colored |
| ターミナル UI | ratatui |
| ターミナル制御 | crossterm |
| コンソールユーティリティ | console |
設計パターン
| パターン | 用途 | 実装 |
|---|---|---|
| Args 構造体 | 型安全な引数 | #[derive(Parser)] |
| サブコマンド | コマンド階層 | #[derive(Subcommand)] |
| 設定レイヤー | 上書きの優先順位 | CLI > env > file |
| 進捗表示 | ユーザーへのフィードバック | ProgressBar::new(len) |
コードパターン:CLI の構造
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(())
}
よくある間違い
| 間違い | ドメイン制約違反 | 修正方法 |
|---|---|---|
| エラーを標準出力に出す | パイプ処理を妨げる | eprintln! |
| ヘルプテキストがない | UX が悪い | #[arg(help = "...")] |
| エラー時にパニックする | 不適切な終了コード | Result と適切な処理 |
| 長時間の処理で進捗を表示しない | ユーザーが状況を把握できない | indicatif |
レイヤー 1 へたどる
| 制約 | レイヤー 2 のパターン | レイヤー 1 の実装 |
|---|---|---|
| 型安全な引数 | Derive マクロ | clap Parser |
| エラー処理 | Result の伝播 | anyhow + 終了コード |
| ユーザーへのフィードバック | 進捗表示の RAII | indicatif ProgressBar |
| 設定の優先順位 | Builder パターン | 階層化されたソース |
関連スキル
| 場合 | 参照先 |
|---|---|
| エラー処理 | m06-error-handling |
| 型駆動の引数 | m05-type-driven |
| 進捗表示のライフサイクル | m12-lifecycle |
| 非同期 CLI | m07-concurrency |
コメント
Markdown 対応