Open11

RustでCLIツールを作ってみた

泡沫京水泡沫京水

RustでCLIツールを作ろう!

なぜ僕は作ろうとしているのか

研究で「作った方が作業が楽だな」と感じたため

泡沫京水泡沫京水

作るものの内容を決めよう!

今回作るもの

  • WebAssemblyモジュール開発時に役立つCLI
    • 開発プロジェクトの作成
      • Rust,Go
        • Rustはwasm-pack
        • GoはTinyGo
    • 開発プロジェクト内のプログラムをコンパイル
      • コンパイルして生成されたファイルは指定したフォルダに格納
泡沫京水泡沫京水

どういうコマンドを打てば動くようにするか

help

  • どういうコマンドを打ったらどの機能が使えるかを表示する

create

  • プロジェクトの作成
    • 引数
      • 言語名(rust,go)
      • プロジェクト名
    • 実行すると、指定した言語でWebAssemblyモジュールを開発するディレクトリやファイルを作成

build

  • プロジェクト内のプログラムをビルド(コンパイル)する
    • 引数
      • コンパイルして生まれるファイルの名前
        • 未指定だとプロジェクト名がついたファイルとしてコンパイル
      • 出力されるファイルを格納するフォルダ名
        • コードが入っているディレクトリと同じ階層に作成されるフォルダの名前を決める
泡沫京水泡沫京水

まず入力された文字をParseしよう!

clapでParseする

コマンドライン引数をParseするためのライブラリとして
clap(Command Line Agrument Parser)を使用します。

cargo add clap@4.4.11 --features derive

まずこんなコード書く

main.rs
+ use clap::Parser;

+ #[derive(Parser)]
+ type ○○ struct {
+    フィールド 型;
+   ・・・
+ }

fn main(){
+     let args = ○○::parse();
}

なんとこの時点でhelpコマンドが自動的に登録される
便利~!!✨

また、Cargo.tomlの内容に合わせて

[package]
name = "example"
version = "1.0.0"
edition = "2021"
+ description = "example project"
・・・
main.rs
use clap::Parser;

#[derive(Parser)]
+ #[command(version,about)]
type ○○ struct {
    フィールド 型;
    ・・・
}

fn main(){
    let args = ○○::parse();
}

こうすると、versionコマンドとCLIの説明文が出てくるようになる

泡沫京水泡沫京水

引数の設定について

structで構造型を作成し、そのフィールドの上に以下のものを与えた時の挙動の対応は以下の通り

  • Vec<~~>:複数の同じ型の値を許容
  • #[arg(short)]: 省略系を許容
    • 例えば、プロパティ名がfで始まるなら、そのプロパティ名か-fを許容する
  • #[arg(default_value_t = ○○)]
    • デフォルト値を設定している
  • #[arg(short,long="lines")]Option<○○>
    • Option型の引数で、長めのコマンド名をlinesとする
泡沫京水泡沫京水

インタラクティブ性を実現する

dialoguerを使って入力を受け取る

  • Select:選択肢を提示して選択してもらうパターン
  • Input:標準入力の受けとり
泡沫京水泡沫京水

カラフルなログを出す!

consoleでANSIエスケープをコードベースで可能にする!

これによって、

  • ターミナル内のカーソル移動(文字列の削除・上書き)
  • 色付き文字出力
  • 絵文字サポート
  • Unicode文字幅を考慮したPadding
泡沫京水泡沫京水

Cargo.tomlに必要な依存パッケージを書いておきたい!

serde&tomlでシリアライズ/デシリアライズして書き込みをする!

これを使って、書いておくべき内容を構造体として指定し、
create実行時に生成されたtomlの内容に追記する形式で情報を追加していく!