clap について
Rust の CLI 開発者向けに、Clap の実装パターンを解説するガイドです。宣言的な引数解析、サブコマンド、設定の優先順位、エラー処理、テスト、シェル補完の生成を扱います。
主な機能
- 01宣言的な引数解析とヘルプ出力
- 02サブコマンドとグローバルフラグの構成
- 03CLI、環境変数、設定ファイルの統合
- 04エラー処理と終了コードの一元化
- 05バイナリのインターフェーステスト
- 06シェル補完の生成
活用シーン
- 01複数のサブコマンドを持つ Rust CLI の構築
- 02設定ソースの優先順位の明確化
- 03引数、出力、終了コードの検証
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ bobmatnyc/ claude-mpm-skills --skill clapインストール後、Claude Code で/clapを入力して読み込まれたか確認します。
同梱ファイルmetadata.json
詳細
Clap(Rust)- 本番環境向け CLI パターン
概要
Clap は、充実したヘルプ出力、検証、サブコマンド対応を備えた宣言的なコマンドライン解析を提供します。予測しやすい UX とテスト可能な実行パスを備えた CLI の構築に使用できます。
クイックスタート
最小限の CLI
✅ 正しい例:derive 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+環境変数+設定ファイル)
明示的な優先順位を設定します。
- CLI フラグ
- 環境変数
- 設定ファイル
- デフォルト値
✅ 正しい例: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();
}
シェル補完(任意)
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で失敗を隠すこと。安定した終了コードと構造化されたエラーを返します。- 1 つのコマンドにフラグを詰め込みすぎること。モードが異なる場合はサブコマンドを使います。
リソース
- Clap:https://docs.rs/clap
- assert_cmd:https://docs.rs/assert_cmd
- clap_complete:https://docs.rs/clap_complete
コメント
Markdown 対応