$ dirsearch --number [num]
こんにちは、@ekusiadadusです。 CLI ツール作っていますか? CLI ツールを Rust で作るときに、毎回環境を整えるのが面倒だったので、テンプレを作りました。 今回はそのテンプレを使って、簡易的な CLI ツールを Rust で爆速で作ってみます。
テンプレートはこちらです。 https://github.com/ekusiadadus/rust-cli-template
今回作るコマンドラインツールはこちらです。 https://github.com/ekusiadadus/dirsearch
今回は、よくあるディレクトリ配下に存在するフォルダ、ファイルの数とその大きさを表示する CLI ツールを爆速で作ってみます。
まずは、テンプレをクローンします。
git clone https://github.com/ekusiadadus/rust-cli-template.git
テンプレを使うには、テンプレのディレクトリに移動して、cargo run
を実行します。
cd rust-cli-template
cargo run
うまくいけば、こんな感じで、rust-cli-template
という名前の CLI ツールが実行されます。
既にこの段階で、cargo run
で CLI ツールが実行できる環境が整っています。
mold + cargo watch は使わなくてもいいですが、以下の点で便利です。
- ホットリロードされる開発環境
- ビルドが速くなる
ここら辺は、参考記事を貼っておくのでもしよかったら使ってみてください。
https://keens.github.io/blog/2021/12/20/moldwotsukautorustnobirudogahayakunaru/
https://qiita.com/kyamamoto9120/items/2081bc44c6c987b9ec29
今回の場合、cargo watch -s 'mold -run cargo run'
でホットリロードできる環境にしています。
Makefile も載せてあるので、make watch
で動きます。
保存すると自動的にビルドされて、実行されます。
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
で実行すると、ディレクトリ配下のファイル、フォルダが表示されます。
ディレクトリ配下のファイルと、フォルダを走査して、ファイルの数と大きさを表示するようにします。 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
で走らせてみるとこんな感じ。
現在のディレクトリ配下には、626 個のファイルが存在して、総合で304742935 bytes
の大きさになることがわかります。
ファイルサイズを 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));
使ってみるとこんな感じ。
ええやん。
ディレクトリ配下のファイルで上位 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(())
}
実行するとこんな感じ。
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();
}
実際に実行するとこんな感じになります。
--number
argument を忘れると怒られます。
例えば、上位 100 件を表示するには、--number 100
とします。
デフォルトで --help
が使えるようになっています。
Cargo.toml
に書いた情報が、--help
で表示されます。
[package]
name = "rust-cli-template"
version = "0.1.0"
edition = "2021"
license = "MIT"
description = "🌸 Rust CLI Template using clap v4 🌸"
readme = "README.md"
homepage = "https://github.com/ekusiadadus/rust-cli-template"
repository = "https://github.com/ekusiadadus/rust-cli-template"
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"