https://zenn.dev/shinobuy/articles/53aed032fe5977

og-base_z4sxah.png

私事

プライベートでRustを勉強しているのですが、 エンジニアとしてもRustasianとしてもまだまだ未熟であるため、技術評論社から出ている実践Rust入門[言語仕様から開発手法まで]をここ数ヶ月ほど写経していました

自分は買うまで気付かなかったんですが(笑)、こちらの書籍が2~3年前のものになります 当然ですが、中で使わているクレートなどが色々とアップデートされていて、 書き換えながら読み進めている感じです 新参者にはなかなか厳しい戦いです😫 しかしながら、書籍は良書で、すごく勉強になってます!

そんな中でも、 この書籍で紹介されているClapというクレートが、個人的にはいい意味でバージョンアップしていたので、自分と同じようなことをしている人のために書籍版とバージョンアップで書き換えた版で比較を載せておこうと思います

そもそもClapについてですが

RustでCLIパーサーを簡単に書くためのクレートです

書籍では、11章『Webアプリケーション, データベース接続』で登場します

このClapのv3から新しくDerive APIというのがサポートされるようになったようで、 それがRustのDeriveをフル活用して、元々のBuilder APIを使うよりシンプルに書けたので、個人的にかなり気に入りました

実際のコード

こちらは書籍で公開されているサンプルコードになります

そして、こちらの内容がDerive APIで書き換えたものになります

use clap::{Parser, Subcommand, ArgEnum};
use reqwest::{blocking};
use std::io;

#[derive(Debug, Parser)]
#[clap(
    name = env!("CARGO_PKG_NAME"),
    version = env!("CARGO_PKG_VERSION"),
    author = env!("CARGO_PKG_AUTHORS"),
    about = env!("CARGO_PKG_DESCRIPTION"),
    arg_required_else_help = true,
)]
struct Cli {
    #[clap(subcommand)]
    subcommand: SubCommands,
    /// server url
    #[clap(short = 's', long = "server", value_name = "URL", default_value = "localhost:3000")]
    server: String,
}

#[derive(Debug, Subcommand)]
enum SubCommands {
    /// get logs
    #[clap(arg_required_else_help = true)]
    Get {
        /// log format
        #[clap(
            short = 'f',
            long = "format",
            required = true,
            ignore_case = true,
            arg_enum,
        )]
        format: Format,
    },
    /// post logs, taking input from stdin
    Post,
}

#[derive(Debug, Clone, ArgEnum)]
enum Format {
    Csv,
    Json,
}

fn main() {
    let cli = Cli::parse();

    let client = blocking::Client::new();
    let api_client = ApiClient { server: cli.server, client };

    match cli.subcommand {
        SubCommands::Get { format } => {
            match format {
                Format::Csv => unimplemented!(),
                Format::Json => do_get_json(&api_client)
            }
        },
        SubCommands::Post => do_post_csv(&api_client)
    }
}

記述量はそんなに変わりませんが、 メソッドチェーンで書いていたオプション(.short, .long)やコマンドの説明(.about)などが移動して、main関数の中はかなりシンプルになったかと思います

補足ですが、 この章で使用されているHTTP Clientクレートreqwestに関しても、 書籍が書かれた時は非同期に対応していませんでしたが、現在は非同期に対応しており同期で使用するためにClient::new()ではなく、blocking::Client::new()でインスタンスを生成する必要がありました

これだけで以下のようなきれいなヘルプ表示を確認することができます

cli 0.1.0
CLI for web server requests.

USAGE:
    cli [OPTIONS] <SUBCOMMAND>

OPTIONS:
    -h, --help            Print help information
    -s, --server <URL>    server url [default: localhost:3000]
    -V, --version         Print version information

SUBCOMMANDS:
    get     get logs
    help    Print this message or the help of the given subcommand(s)
    post    post logs, taking input from stdin

オプションやサブコマンドを受け取るためのStructEnumが新たに増え、 これらに対して#[derive]アトリビュートと#[clap]アトリビュートでコマンドやオプションを設定していきます