Amedeo
SYMBIOSYS OF
PURPOSE & STYLE
Follow us

Search

ekusiadadus
  -  Programming   -  Let’s build an environment for creating command line tools in Rust at blazing speed, 2022 edition.
Rustでコマンドラインツールを爆速で作る

Hi, @ekusiadadus here.
Are you creating CLI tools?
I’ve made a template for creating CLI tools in Rust, because it was a pain to set up the environment each time.
This time, I’ll use that template to make a simple CLI tool in Rust in a blazing fast way.

Here is the template.

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

Here is the command line tool we will create.

https://github.com/ekusiadadus/dirsearch

CLI tools registered and distributed at https://crates.io/.

https://crates.io/crates/dirsearch

It can be installed with cargo install dirsearch.

image.png
It can be installed with cargo install dirsearch.

Creating a CLI tool using templates

In this article, we will try to make a CLI tool that displays the number and size of folders and files that exist under common directories in a very fast way.

Cloning a Template

First, clone the temps.

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

Try running it with cargo run

To use temps, go to the temps directory and run cargo run.

cd rust-cli-template
cargo run

If all goes well, the CLI tool named rust-cli-template will be run like this.

At this stage, the environment is already ready to run the CLI tool with cargo run.

image.png

FYI Use mold + cargo watch

You do not have to use mold + cargo watch, but it is useful in the following ways

  • hot reload
  • fast build

I’ll post some reference articles around here if you’d like to use them.

moldを使うとRustのビルドが速くなる | κeenのHappy Hacκing Blog

moldとcargo watchを併用してより快適な開発環境を作る – Qiita

In this case, I used cargo watch -s ‘mold -run cargo run’ to create a hot reloadable environment.
Makefile is also included, so make watch will work.

After saving the file, it will automatically build and run.

build-with-mold.gif

Displaying the number and size of files and folders under a directory

Use walkDir to display the number and size of files and folders under a directory.

Install walkDir

walkdir

cargo add walkdir

To use walkDir, add use walkdir::WalkDir;.

Obtain files and folders under a directory

use walkdir::WalkDir;

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

When run with cargo run, files and folders under the directory are displayed.

image.png

Display the number and size of files and folders under a directory

To display the number and size of files and folders under a directory by traversing them.
Using walkdir, it is very easy to scan files and folders.

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);
}

This is what it looks like when you actually run it with CARGO RUN.

image.png

We can see that there are 626 files under the current directory, with an overall size of 304742935 bytes.

(aside 2) To display the file size in a nice way in Rust…

To display the file size in a nice way in Rust, use 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");

This is a crate that displays the file size in a nice way, like this.

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

Here’s what it looks like when used.

image.png

Yeah, right.

Bring up the top N files under the directory

directory to bring in the top N files.
Also, main is getting too large, so we will cut it out into a function.

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(())
}

When executed, it looks like this.

image.png

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

Use Clap to make it a command line tool

clap v4 is used to make it a command line tool. v4 is quite different from v3, so please refer to the clap v4 documentation as you proceed.

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();
}

When actually executed, it looks like this.

image.png

--If you forget the number argument, you will be offended.

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

Now that we have the command line tool, we register it with https://crates.io/ so that we can actually cargo install dirsearch.

Prepare for release with cargo release –dry-run.

image.png

If successful, let’s actually install and run it locally.

cargo install –path . You can install locally with –path .
With dirsearch, you can use the command line tool.

image.png

Confirm that each command actually works.

image.png

It seems to be working properly.

image.png

It is actually working and will be distributed.

image.png

We have confirmed that the registration was successful.

image.png

Now I can cargo install dirsearch from anywhere!

image.png

summary

Let’s build an environment for creating command line tools in Rust at blazing speeds, 2022 edition, so how was it?

https://crates.io/crates/dirsearch

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

GitHub – ekusiadadus/dirsearch: This is a command line tool to search …

Above is the actual repository of the command line tool we distributed this time.
It’s so easy!

GitHub – ekusiadadus/rust-cli-template: A Private Template Repository …

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

仕事中にワールドカップをばれないように見るCLI ツールを Rust で作ってみた – Qiita

GitHub – ekusiadadus/samuraicup: A CLI tool to get World Cup 2022 twee…

GitHub – ekusiadadus/twisort

This area is completely for my own use since I’m using the Twitter API, but it’s a lot of fun to make a command line tool in Rust!
The specs have changed quite a bit since clap v4, so it may be tough to get used to clap, but you can use templates to make it a blast, so please give it a try!

Leave a Comment