LoginSignup
2
1

More than 1 year has passed since last update.

Rustでコマンドラインツールを爆速で作る

Last updated at Posted at 2022-12-16

こんにちは、@ekusiadadusです。
CLI ツール作っていますか?
CLI ツールを Rust で作るときに、毎回環境を整えるのが面倒だったので、テンプレを作りました。
今回はそのテンプレを使って、簡易的な CLI ツールを Rust で爆速で作ってみます。

テンプレートはこちらです。

今回作るコマンドラインツールはこちらです。

https://crates.io/ に登録して配布した CLI ツールです。

cargo install dirsearchでインストールできます。

image.png

テンプレを使って CLI ツールを作る

今回は、よくあるディレクトリ配下に存在するフォルダ、ファイルの数とその大きさを表示する CLI ツールを爆速で作ってみます。

テンプレをクローンする

まずは、テンプレをクローンします。

git clone https://github.com/ekusiadadus/rust-cli-template.git

cargo run で実行してみる

テンプレを使うには、テンプレのディレクトリに移動して、cargo runを実行します。

cd rust-cli-template
cargo run

うまくいけば、こんな感じで、rust-cli-templateという名前の CLI ツールが実行されます。

既にこの段階で、cargo run で CLI ツールが実行できる環境が整っています。

image.png

(余談) mold + cargo watch を使う

mold + cargo watch は使わなくてもいいですが、以下の点で便利です。

  • ホットリロードされる開発環境
  • ビルドが速くなる

ここら辺は、参考記事を貼っておくのでもしよかったら使ってみてください。

今回の場合、cargo watch -s 'mold -run cargo run' でホットリロードできる環境にしています。
Makefile も載せてあるので、make watch で動きます。

保存すると自動的にビルドされて、実行されます。

build-with-mold.gif

ディレクトリ配下のファイル、フォルダの数と大きさを表示する

walkDir を使って、ディレクトリ配下のファイル、フォルダの数と大きさを表示するようにします。

walkDir をインストールする

walkdirをつかいます。

cargo add walkdir

walkDir を使うには、use walkdir::WalkDir; を追加します。

ディレクトリ配下のファイル、フォルダを取得する

use walkdir::WalkDir;

fn main() {
    for entry in WalkDir::new(".") {
        let entry = entry.unwrap();
        println!("{}", entry.path().display());
    }
}

cargo run で実行すると、ディレクトリ配下のファイル、フォルダが表示されます。

image.png

ディレクトリ配下のファイル、フォルダの数と大きさを表示する

ディレクトリ配下のファイルと、フォルダを走査して、ファイルの数と大きさを表示するようにします。
walkdir を使うと非常に簡単にファイルとフォルダを走査できます。

use walkdir::WalkDir;
const DIR: &str = "./";

fn main() {
    let mut size: u64 = 0;
    let mut count: u64 = 0;

    for entry in WalkDir::new(DIR).into_iter().filter_map(|e| e.ok()) {
        let path = entry.path();
        if path.is_file() {
            size += path.metadata().unwrap().len();
            count += 1;
        }
        println!("{}", entry.path().display());
    }

    println!("{} files, {} bytes", count, size);
}

実際にcargo run で走らせてみるとこんな感じ。

image.png

現在のディレクトリ配下には、626 個のファイルが存在して、総合で304742935 bytesの大きさになることがわかります。

(余談 2) ファイルサイズを Rust でいい感じに表示するには...

ファイルサイズを Rust でいい感じに表示するには、file_sizeを使います。

use file_size::fit_4;

assert_eq!(&fit_4(999), "999");
assert_eq!(&fit_4(12345), "12K");
assert_eq!(&fit_4(999_999), "1.0M");
assert_eq!(&fit_4(7_155_456_789_012), "7.2T");

こんな感じで、いい感じにファイルサイズを表示してくれるクレートです。

println!("{} files, {} bytes", count, fit_4(size));

使ってみるとこんな感じ。

image.png

ええやん。

ディレクトリ配下のファイルで上位 N 件を持ってくる

ディレクトリ配下のファイルで上位 N 件を持ってくるようにします。
あと main が大きくなってきたので、関数に切り出します。

fn get_dir_size(dir: &str) -> Result<(), Box<dyn Error>> {
    let mut size: u64 = 0;
    let mut count: u64 = 0;
    let mut tops: Vec<Entry> = Vec::with_capacity(NUM + 1);
    let mut min_tops: u64 = 0;

    for entry in WalkDir::new(dir).into_iter().filter_map(|e| e.ok()) {
        let path = entry.path();
        if path.is_file() {
            let t = path.metadata().unwrap().len();
            if t > min_tops {
                tops.push(Entry {
                    path: path.to_str().unwrap().to_string(),
                    size: t,
                });
                tops.sort_by(|a, b| b.size.cmp(&a.size));
                tops.truncate(NUM);
                min_tops = tops.last().unwrap().size;
            }
            size += path.metadata().unwrap().len();
            count += 1;
        }
    }

    println!("{} files, {} bytes", count, fit_4(size));
    println!("{} largest files:", NUM);
    println!("{} | {}", "Size", "Path");
    for t in tops {
        println!("{} | {}", fit_4(t.size), t.path);
    }

    Ok(())
}

実行するとこんな感じ。

image.png

ディレクトリ配下のファイルで上位 N 件を持ってくる (並列処理)

Clap を使って、コマンドラインツールにする

clap v4 を使って、コマンドラインツールにします。
v4 は、v3 とはかなり違うので、clap v4 のドキュメントを見ながら進めましょう。

use clap::Parser;

#[derive(Parser)]
#[command(author, version, about, long_about = None)] // Read from `Cargo.toml`
struct Cli {
    #[arg(long)]
    number: usize,
}

fn main() {
    let cli = Cli::parse();
    let num = cli.number;
    let dir = DIR;

    if num == 0 {
        println!("Number of files to show must be greater than 0");
        return;
    }

    get_dir_size(dir, num).unwrap();
}

実際に実行するとこんな感じになります。

image.png

--number argument を忘れると怒られます。

image.png

例えば、上位 100 件を表示するには、--number 100とします。

image.png

デフォルトで --help が使えるようになっています。

image.png

Cargo.toml に書いた情報が、--help で表示されます。

[package]
name = "dirsearch"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "🌸 Rust CLI Template using clap v4 🌸"
readme = "README.md"
homepage = "https://github.com/ekusiadadus/dirsearch"
repository = "https://github.com/ekusiadadus/dirsearch"
keywords = ["cli", "Japan", "Rust"]
categories = ["command-line-utilities"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = { version = "4.0.29", features = ["derive"] }
file-size = "1.0.3"
walkdir = "2.3.2"

dirsearch を cretes.io に登録して配布する

https://crates.io/

image.png

コマンドラインツールができたので、実際に cargo install dirsearch できるようにhttps://crates.io/に登録します。

cargo release --dry-run で、リリースの準備をします。

image.png

成功したら、実際にローカルでインストールして動かしてみましょう。

cargo install --path . で、ローカルにインストールできます。
dirsearch で、コマンドラインツールが使えるようになります。

image.png

各コマンドが実際に動く確認します。

image.png

ちゃんと動いていそう。

image.png

実際に動いているので、配布します。
cargo publish で、https://crates.io/に登録できます。

image.png

登録できたことが確認できました。

image.png

これで、どこからでもcargo install dirsearchできますね!

image.png

まとめ

Rust でコマンドラインツールを爆速で作る環境を構築しよう 2022 年版ということで、いかがだったでしょうか?

テンプレを使ってを配布するまでに 1 時間かからず行えました!

上が今回実際に配布したコマンドラインツールのリポジトリです。
めちゃくちゃかんたんですよね!

他にも、Rust で作ったコマンドラインツールをテンプレから作っています。

ここら辺は、Twitter API を使っているので完全に自分用ですが、Rust でコマンドラインツールを作るのはとても楽しいですね!
clap v4 からかなり仕様が変わったので、結構 clap になれるのが大変かもしれませんが、テンプレを使って爆速で作れるので、ぜひ試してみてください!

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1