開発

[rust] clap v4で書き換えてみる

空き時間ができたので久しぶりに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 versionCode language: PHP (php)
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(())
}
Code language: PHP (php)

位置引数を使うには、単に#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);

...以下略Code language: PHP (php)
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
Code language: CSS (css)
$ ./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
管理人

Recent Posts

情報セキュリティマネジメント試験取得への道

スキルアップを図るべく情報セキ…

2か月 ago

ファイナンシャルプランナー3級試験取得への道

スキルアップを図るべくファイナ…

2か月 ago

[rust] New Type Patternを使ってみる

DDDの考えを取り入れることで…

5か月 ago

RustでDDDの要素を取り入れてみる

前回SOLID原則というものを…

5か月 ago

期間限定!書籍無料キャンペーン2025

「mdBookではじめるKin…

5か月 ago