Rustのコマンドラインパーサーのxflagsを試す
RustのLSであるrust-analyzerのコードを眺めていて(今作っているユーティリティのために他のRust製のLSの依存関係とかコードを調査していた)、xtaskの依存関係の中にxflagsが入っていることに気が付いてコードを見たかんじ便利そうだから試してみる。
matklad/xflagsにxflagsのコードはある。また今回作成したコードはsatler-git/sandbox内にある。
注意
Rustの有名なコマンドラインパーサーにclapがある。どちらを選べばいいのかはxflagsのドキュメントにこう記載がある。
if you need all of the features and don’t care about minimalism, use clap.
if you want to be maximally minimal, need only basic features (eg, no help generation), and want to be pedantically correct, use lexopt.
if you want to get things done fast (eg, you want auto help, but not at the cost of waiting for syn to compile), consider this crate.
雑訳
もし完全な機能が欲しくてミニマリズムにもこだわらないなら、clapを使う。
もし最大限にミニマル(最小限の機能しかなくhelpの生成などが必要ない場合)、そして厳密な正確性が欲しい場合、lexoptを使う。
もし事が素早く作業を終わらせたい(helpの生成は欲しいけどsynのコンパイルを待つコストを払いたくない)、このクレートを検討する。
ようはxtaskみたいなものに使われるように設計されているということ。
基本
この記事で使っているRustのバージョンは以下の通り
sandbox/rust
❯ cargo --version
cargo 1.82.0 (8f40fc59f 2024-08-21)
sandbox/rust
❯ rustc --version
rustc 1.82.0 (f6e511eec 2024-10-15)
またxflagsのバージョンは0.3.2。
xflagsはproc-macroを使うことでパーサーを生成したりできる。xflagsのマクロにはxflags::xflags!
とxflags::parse_or_exit!
があり、前者はパーサーのコードが生成され後者はパーサーのコードが生成されその場でperseして構造体を返す。xflags::xflags!
でparse_or_exit!
と同じ機能を実現するには(例えば)flags
モジュールにxflags!
マクロを書いてそこに生成された構造体のfrom_env_or_exit
関数を呼び出して利用する。
またそれぞれのマクロで特殊な構文を使用してコマンドを定義する。
実践
まずparse_or_exit!
を追加する。
fn main() {
let flags = xflags::parse_or_exit! {};
}
必須の引数を定義する
必須の引数を定義するにはマクロのなかでまずrequired
としてから名前と型を指定する。ヘルプの生成に使用されるドキュメンテーションコメントも記載する必要がある。
fn main() {
let flags = xflags::parse_or_exit! {
/// num to succ
required num: u32
};
}
オプションを定義する
さらにオプションを定義するには先頭にoptional
を付けることが出来る。
+ /// Decrease the input number
+ optional -d, --decrease
そして機能を足したコードは以下のようになる。
fn main() {
let flags = xflags::parse_or_exit! {
/// Decrease the input number
optional -d, --decrease
/// num to succ
required num: u32
};
if flags.decrease {
println!("{}", flags.num.checked_sub(1).unwrap_or_default());
} else {
println!("{}", flags.num + 1);
}
}
実行すると以下のよう。分かるようにビルドはとても早かった(マシンによって差は出るが自分のマシンの場合は0.29sとなっている)。またヘルプが自動的に生成されている。
sandbox/rust/try-xflags
❯ cargo run --bin succ -- 1
Compiling xflags v0.3.2
Compiling try-xflags v0.1.0 (/home/satler/repos/github.com/satler-git/sandbox/rust/try-xflags)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.29s
Running `target/debug/succ 1`
2
sandbox/rust/try-xflags
❯ ./target/debug/succ 1 --decrease
0
sandbox/rust/try-xflags
❯ ./target/debug/succ --help
ARGS:
<num>
num to succ
OPTIONS:
-d, --decrease
Decrease the input number
-h, --help
Prints help information.
またcargo-expandした結果のgistを載せておく。
サブコマンド、repeated
サブコマンドはcmd
キーワードを用いて作成できる。また複数名前を書くことでエイリアスを追加できる。しかし実践はしなかったから詳しくはドキュメントを見て欲しい。
fn main() {
let flags = xflags::parse_or_exit! {
cmd run r exec {}
}
}
おわり
まだxtaskを活かせるような大きなプロジェクトは出来ていないがそのうち活用していきたい。シンプルに記述できてやりやすかった。