日本の山岳一覧・百名山 API を開発しましたCLICK !

Rust | clap v3系でCLIツールを作成する

Writing a CLI Tool in Rust with Clap
  • URLをコピーしました!

Rust で簡単な CLI ツールを作成してみました。

CLI ツールを作成するなら、clap が便利

  1. clap を使えば、簡単に CLI ツールが作成できる
  2. 見栄えを良くするために ansi_term で色付きの標準出力
目次

Webページの応答時間を監視するCLIツール

今回は、応答時間を監視したい Web ページの URL を引数に持つ CLI ツールを作りました。

monitをターミナルで実行

v0.2.0-alpha 以降では、ファイル出力に対応するようにアップデートを加えてます。

使用するクレート

cargo.toml は以下のようになっています。

[dependencies]
ansi_term = "0.12.1"
clap = { version = "3.2.16", features = ["derive"] }
chrono = "0.4.20"
reqwest = "0.11.11"
tokio = { version = "1.20.1", features = ["full"] }
ansi_term

ターミナルでの標準出力時に色をつけるために使用します。

clap

今回の主役。CLIツールの作成を簡単にしてくれるクレート、Rustの定番コマンドラインパーサーです。

chrono

日時を扱うためのクレート。

reqwesttokio

非同期処理で HTTP リクエストを飛ばすために使用します。

clap でコマンドの引数を定義

derive ベースのマクロを使った記述方法で実装します。

マクロを追加していくことで簡単にコマンドラインの引数パーサーを実装できます。

// name = "monit" でパッケージ名とは異なる名前を定義
// about は cargo.toml の [package] description を出力してくれる
// arg_required_else_help = true で必須の引数なしで実行した場合に --help の内容を出力してくれる
#[derive(Parser, Debug)]
#[clap(name = "monit", version, about, long_about = None, arg_required_else_help = true)]
struct Args {
    // 必須の定位引数
    /// URL to be monitored for response time
    url: String,

    // 初期値ありのオプション引数
    // value_name = "" で --help の <> の出力内容を変更
    // default_value_t で初期値を定義
    /// Interval seconds
    #[clap(
        short,
        long,
        value_name = "INTERVAL SECONDS",
        default_value_t = 30
    )]
    interval: u64,

    // 初期値ありのオプション引数
    // 列挙型 Output を出力形式として指定できるように arg_enum を追加
    // value_name = "" で --help の <> の出力内容を変更
    // default_value で初期値を定義
    /// Output type
    #[clap(
        short,
        long,
        arg_enum,
        value_name = "OUTPUT TYPE",
        default_value = "text"
    )]
    output: Output,
}

// ArgEnum でオプション引数の列挙型にできる
#[derive(Debug, Clone, ArgEnum, Copy)]
enum Output {
    Csv,
    Json,
    Text,
}

初期値を定義する場合、Option型だとコンパイル時にエラーが発生した。

初期値の定義があるためツール実行時の引数の指定は省略可能だけど、あくまで”必須”のオプション引数であるためだと思われます。

–helpの実行結果

–help もしくは 引数 <URL> なしで実行した場合、以下のように出力されます。

monit 0.1.0
Monitor website response time.

USAGE:
    monit [OPTIONS] <URL>

ARGS:
    <URL>    URL to be monitored for response time

OPTIONS:
    -h, --help
            Print help information

    -i, --interval <INTERVAL SECONDS>
            Interval seconds [default: 30]

    -o, --output <OUTPUT TYPE>
            Output type [default: text] [possible values: csv, json, text]

    -V, --version
            Print version information

deriveベースのサンプルコード

clap のマクロについては Github にサンプルコードがあるので、参考にしてみてください。

reqwest::getのレスポンスが返るまでの時間を計測する

GET の HTTP リクエストを実行しレスポンスが返ってくるまでにかかる時間を Web ページの応答速度として計測していきます。

let start = Instant::now();
let resp = reqwest::get(url).await;
let end = start.elapsed();

応答速度の出力は、下3桁までの秒数としました。

let response_time = format!(
    "{}.{:03}",
    self.elapsed.as_secs(),
    self.elapsed.subsec_nanos() / 1_000_000
);

ansi_termで標準出力に色を付ける

reqwest::StatusCode のステータスコードが200番台の場合は成功として緑色で、それ以外は赤色で標準出力できるようにします。

ステータスコードが200番台かの判定は構造体 StatusCode に定義されている is_success() でおこなっています。

let color_msg = if status_code.is_success() {
    Colour::Green.paint(&msg)
} else {
    Colour::Red.paint(&msg)
};

loopとsleep

監視を目的とした CLI ツールなので、loop で無限に HTTP リクエストを飛ばすようにします。

loop で無限ループが発生しているので、CLI ツールを停める場合はCtrl + Cで停止させる必要があります。

また、インターバルなしで無限にWebページにアクセスしすぎるのは危険なので、スリープを挟みます。

sleep(Duration::from_secs(interval));

負荷をかけることを目的とする場合はインターバル0秒で実行すると良いかもしれません笑

Windows 10では enable_ansi_support() が必要?

Windows 10環境でビルドしてコマンドプロンプト cmd.exe で実行すると、色付きの出力が正しく機能しません。

crates.io の ansi_term のページには以下のように注意書きがあります。

Note for Windows 10 users: On Windows 10, the application must enable ANSI support first:

https://crates.io/crates/ansi_term
let enabled = ansi_term::enable_ansi_support();

ansi_term::enable_ansi_support 関数を呼び出すことで、Windows 10 で ANSI コードのサポートを有効にできるようです。

この関数をMacの開発環境で実行するとコンパイルに失敗します。

Windows環境ではコンパイルする(?) みたいな制御がいるのでしょうか?

まだ解決策が分かっていません…

let _ = ansi_term::enable_ansi_support();
                    ^^^^^^^^^^^^^^^^^^^ not found in `ansi_term`

まとめ

clap を使えば、簡単に CLI ツールを作成することができます。

また、CSVやJSON形式での出力メッセージをformat!で実装してしました。

serdecsv などのクレートを使用すれば簡単に文字列に変換したり凝ったこともできたりしそうですが、今回の規模のツールであれば不要かなと判断しました。

あの Meta も、CLI ツールの作成は Rust での実装をおすすめしているようです!

ご参考までに;-)

For CLI tools, we recommend Rust. This is a new recommendation for this year.

https://engineering.fb.com/2022/07/27/developer-tools/programming-languages-endorsed-for-server-side-use-at-meta/

参考

著:初田直也, 著:山口聖弘, 著:吉川哲史, 著:豊田優貴, 著:松本健太郎, 著:原将己, 著:中村謙弘, 著:フォルシア株式会社
¥3,208 (2024/11/15 19:47時点 | Amazon調べ)
Writing a CLI Tool in Rust with Clap

この記事が気に入ったら
フォローしてね!

  • URLをコピーしました!

コメント

コメントする

目次