空き時間ができたので久しぶりにrustの勉強です。
文法など少し忘れているので以前に書いたコードをいじってみました。
コマンドラインのオプション処理に使用していたclapがバージョンアップと共に以前のコードでは動かなくなっていたので書き直してみました。
どうやらメジャーバージョンアップ毎に書き方が変化しているようです。rustのDeriveによりシンプルな書き方に変わっているのでしょう。
一応以前のBuilderタイプも使用できるようです。おそらく今後はサポートされない方向性だと思います。
Deriveタイプで書き直してみました。以前のclap v2よりすっきりしましたね。
Deriveを使うには、
$ cargo add clap --features derive
で、有効化して下記例のようにArgsを設定すればオプションを定義できます。
この例では、ショート形式オプションが-d、ロング形式が–dir、デフォルト値がカレントフォルダを表すドット( . )、スラッシュ3つのコメントがそのままコマンドの説明文として使用されます。
下記はコマンドヘルプの出力例です。
また、位置引数(positional arguments)も使えます(こちらがデフォルト。後述)
Usage: test_clapv4 [OPTIONS]
Options:
-d, --dir <DIR> target directory [default: ./]
-h, --help Print help
-V, --version Print version
use anyhow::Result; // 1.0.71
use clap::Parser; // 4.3.11
use std::fs;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// target directory
#[arg(short, long, default_value_t = String::from("./") )]
dir: String,
}
fn main() -> Result<()> {
let args = Args::parse();
let dir_string = args.dir.to_string();
println!("{}", dir_string);
let _ = get_dirs(dir_string);
Ok(())
}
fn get_dirs(dir: String) -> Result<()> {
let mut entries = fs::read_dir(dir)?;
while let Some(entry) = entries.next() {
let entry = entry?;
let metadata = entry.metadata()?;
let path = entry.path();
if metadata.is_dir() {
let path = path.display().to_string();
println!("{}", path);
let _ = get_dirs(path);
continue;
}
if let Ok(symlink) = fs::read_link(&path) {
if path.is_dir() {
println!("{}@ -> {}", path.display(), symlink.display());
}
}
}
Ok(())
}
位置引数を使うには、単に#arg(short)や(long)アトリビュートを付与しなければそのように扱われます。
そのまま型を割り当てると実行時に引数が必須になるので、Optionalで包んで上げたほうがよいでしょう。
デフォルトで#[arg(required = true)]になっているようです。#[arg(required = false)]で型がOptionalで包んでいない場合もコンパイルはできますが型の情報が優先されて必須扱いになります。そのため、required はわざわざ指定しなくても良いかもしれません。
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// target directory
#[arg(short, long, default_value_t = String::from("./dir") )]
dir: String,
/// some option#1
#[arg(required = false)]
opt1: Option<u8>,
/// some option#2
opt2: Option<u8>,
/// some option#3
#[arg(required = false)]
opt3: Option<u8>,
/// specify some config
#[arg(short, long, required = false)]
config: Option<String>,
}
fn main() -> Result<(), Error> {
let args = Args::parse();
let dir_string = args.dir.to_string();
println!("opt1: {:?}", args.opt1);
println!("opt2: {:?}", args.opt2);
println!("opt3: {:?}", args.opt3);
...以下略
Usage: test_clapv4 [OPTIONS] [OPT1] [OPT2] [OPT3]
Arguments:
[OPT1] some option#1
[OPT2] some option#2
[OPT3] some option#3
Options:
-d, --dir <DIR> target directory [default: ./dir]
-c, --config <CONFIG> specify some config
-h, --help Print help
-V, --version Print version
$ ./test_clapv4 1
opt1: Some(1)
opt2: None
opt3: None
./dir
./dir/a@ -> aaa
./dir/c@ -> ccc
./dir/b@ -> bbb
./dir/aaa
./dir/ccc
./dir/bbb