Skip to content

Handle CLI arguments

コマンドラインインターフェイス (CLI) ツールは、多くの場合、動作を決定するために入力パラメーターを必要とします。 clap (コマンドライン引数パーサー) は、これらの引数の解析を直感的に促進する機能が豊富なRustライブラリです。

コマンドライン引数の定義

このスニペットでは、 clap ライブラリを使用して、 Args structを定義します。これは、アプリケーションに渡された引数をキャプチャして構成するために使用されます。

use clap::Parser;
#[derive(Parser, Debug)]
#[command(version = version(), about = "ratatui template with crossterm and tokio")]
struct Args {
/// App tick rate
#[arg(short, long, default_value_t = 1000)]
app_tick_rate: u64,
}

ここで、Args 構造体は 1 つのコマンドライン引数を定義します:

  • app_tick_rate: アプリケーションのティック レートを指定します。

これにはデフォルト値が用意されているため、ユーザーがこの引数を指定しなくても、アプリケーションはデフォルト値で続行できます。

バージョン情報の表示

CLI における一般的な慣例の 1 つは、バージョン情報を表示する機能です。ここでは、バージョン情報は Git コミット ハッシュを含むさまざまなパラメータの組み合わせとして表示されます。

スニペットに示されているように、version() 関数はこの情報を取得します。

pub fn version() -> String {
let author = clap::crate_authors!();
let commit_hash = env!("RATATUI_TEMPLATE_GIT_INFO");
// let current_exe_path = PathBuf::from(clap::crate_name!()).display().to_string();
let config_dir_path = get_config_dir().unwrap().display().to_string();
let data_dir_path = get_data_dir().unwrap().display().to_string();
format!(
"\
{commit_hash}
Authors: {author}
Config directory: {config_dir_path}
Data directory: {data_dir_path}"
)
}

この関数は、XDG ディレクトリのセクションget_data_dir()get_config_dir() を使用します。

この関数は、環境変数 RATATUI_TEMPLATE_GIT_INFO も使用して、Git コミット ハッシュを導出します。この変数は、ビルド プロセス中に build.rs によって設定できます。

println!("cargo:rustc-env=RATATUI_TEMPLATE_GIT_INFO={}", git_describe);

--version フラグを使用して CLI ツールを呼び出すと、作成者、コミット ハッシュ、構成ディレクトリとデータ ディレクトリへのパスを含むバージョンの詳細がユーザーに表示されます。

--version output

version() 関数の出力は単なる例です。上記の文字列テンプレートコードを修正することにより、コンテンツを簡単に調整できます。

参照のための完全な build.rs は次のとおりです。

fn main() {
let git_output = std::process::Command::new("git").args(["rev-parse", "--git-dir"]).output().ok();
let git_dir = git_output.as_ref().and_then(|output| {
std::str::from_utf8(&output.stdout).ok().and_then(|s| s.strip_suffix('
').or_else(|| s.strip_suffix("
")))
});
// Tell cargo to rebuild if the head or any relevant refs change.
if let Some(git_dir) = git_dir {
let git_path = std::path::Path::new(git_dir);
let refs_path = git_path.join("refs");
if git_path.join("HEAD").exists() {
println!("cargo:rerun-if-changed={}/HEAD", git_dir);
}
if git_path.join("packed-refs").exists() {
println!("cargo:rerun-if-changed={}/packed-refs", git_dir);
}
if refs_path.join("heads").exists() {
println!("cargo:rerun-if-changed={}/refs/heads", git_dir);
}
if refs_path.join("tags").exists() {
println!("cargo:rerun-if-changed={}/refs/tags", git_dir);
}
}
let git_output =
std::process::Command::new("git").args(["describe", "--always", "--tags", "--long", "--dirty"]).output().ok();
let git_info = git_output.as_ref().and_then(|output| std::str::from_utf8(&output.stdout).ok().map(str::trim));
let cargo_pkg_version = env!("CARGO_PKG_VERSION");
// Default git_describe to cargo_pkg_version
let mut git_describe = String::from(cargo_pkg_version);
if let Some(git_info) = git_info {
// If the `git_info` contains `CARGO_PKG_VERSION`, we simply use `git_info` as it is.
// Otherwise, prepend `CARGO_PKG_VERSION` to `git_info`.
if git_info.contains(cargo_pkg_version) {
// Remove the 'g' before the commit sha
let git_info = &git_info.replace('g', "");
git_describe = git_info.to_string();
} else {
git_describe = format!("v{}-{}", cargo_pkg_version, git_info);
}
}
println!("cargo:rustc-env=RATATUI_TEMPLATE_GIT_INFO={}", git_describe);
}