CLI, which is generally referred to as Command Line Interface, is a terminal or console interface that allows the user to interact with software or computer applications by typing text commands into the terminal without using a GUI (Graphical User Interface). In a CLI environment, a user inputs specific commands to instruct the program to perform tasks such as file manipulation, system monitoring, running commands on cloud services, or program execution.
In this article, you will learn how to build a Command Line Interface (CLI) using the Rust programming language. This CLI tool will scan a specified directory and arrange files into subdirectories based on their file extensions. This project will help you understand the process of building a CLI tool in Rust.
Table of Contents
-
- Prerequisites
- Setting Up the Rust Project
- Implementing the CLI Tool
- Testing the Tool
- Dockerizing the Application
- Conclusion
Prerequisites
Before we start, ensure you have the following software or programs installed on your system:
- Rust and Docker
- A terminal or access to a command prompt
- Basic Knowledge of programming
In case you do not have Rust or Docker installed. You can follow the instructions below on how to install this software.
Below is a step-by-step guide on how to create a file organizer CLI tool in Rust.
Step 1: Setting Up Your Environment: Install Rust and Docker
Firstly, ensure you have both Docker and Rust installed in your development environment. Run the following commands in your terminal to install Docker and Rust. The command installed , rustup
which helps to install the latest stable version of Rust.
# To install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# To install Docker
# Follow this link https://docs.docker.com/engine/install/
Step 2: Setting Up the Rust Project
You will create a new Rust project using cargo
Rust’s package manager. Open your terminal and run the command below
cargo new file_organizer_cli_tool_rust
cd file_organizer_cli_tool_rust
Step 3: Implementing the CLI Tool
This will create a new directory file_organizer
containing the necessary files for our Rust project. You can now implement the core functionality for your file organizer, open the file src/main.rs
in your favourite code editor, and replace it with the content in the code snippet below.
use clap::{Arg, Command};
use std::fs;
use std::path::{Path};
use std::error::Error;
fn organize_files(dir: &Path) -> Result<(), Box<dyn Error>> {
// Iterate over entries in the specified directory
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
// Check if the entry is a file
if path.is_file() {
// Get the file extension
if let Some(extension) = path.extension() {
let ext_str = extension.to_string_lossy();
let dest_dir = dir.join(ext_str.as_ref());
// Create a directory for the extension if it doesn't exist
if !dest_dir.exists() {
fs::create_dir(&dest_dir)?;
}
// Move the file into the corresponding directory
let new_path = dest_dir.join(path.file_name().unwrap());
fs::rename(&path, new_path)?;
println!("Moved: {:?}", path.file_name().unwrap());
}
}
}
Ok(())
}
fn main() -> Result<(), Box<dyn Error>> {
let matches = Command::new("File Organizer")
.version("1.0")
.about("Organizes files in a directory by their extensions")
.arg(
Arg::new("directory")
.help("The directory to organize")
.required(true)
.index(1),
)
.get_matches();
let dir = matches.get_one::<String>("directory").unwrap();
let path = Path::new(dir);
if path.exists() && path.is_dir() {
organize_files(&path)?;
println!("Files organized successfully.");
} else {
eprintln!("Provided path is not a valid directory.");
}
Ok(())
}
Code Explanation
The Rust code above is a simple command-line application that organizes files into a specified directory based on the extensions of the files. Here is a breakdown of the code.
use clap::{Arg, Command};
use std::fs;
use std::path::{Path, PathBuf};
use std::error::Error;
clap:
It is used for parsing command-line arguments in the Rust programming language.std::fs:
It is a module for working with the filesystem for reading directories and moving files.std::path::{Path, PathBuf}:
It is an inbuilt method in Rust for manipulating filesystem paths.std::error::Error:
It is a trait used in Rust for error handling in Rust.
let matches = Command::new("File Organizer")
.version("1.0")
.about("Organizes files in a directory by their extensions")
.arg(
Arg::new("directory")
.about("The directory to organize")
.required(true)
.index(1),
)
.get_matches();
The command snippet above is used for accepting command-line arguments using clap
. The program takes one required argument called directory
Step 4: Add the dependency in Cargo.toml file
Open your cargo.toml
file and replace the content with the code snippet below. cargo.toml
contains the dependency for running the file organizer CLI tool. You will be using it clap
as a dependency in your Rust project, which is a command-line argument in Rust. For more information, check https://docs.rs/clap/latest/clap/
[package]
name = "file_organizer_cli_tool_rust"
version = "0.1.0"
edition = "2021"
[dependencies]
clap = "4.5.20"
Step 5: More Code Explanation
- Getting Command Line Argument: The program accepts the directory path as part of the command line argument from the terminal.
- Reading the Directory: The code reads the specified directory and checks for the files and their extension of the files.
- Organizing Files: For each file in the directory, it reads the extension and creates a corresponding subdirectory with the extension name (for example it creates if the file is log.txt, it will create a subdirectory called
txt
If it does not exist, it moves the file into an appropriate subdirectory.
Step 6: Create the Dockerfile
Create a file called Dockerfile
in your root project. The filename will be Dockerfile
. Copy and paste the code snippet below for a copy of the Dockerfile
FROM rust:alpine3.20 as builder
# Install necessary build dependencies
RUN apk add --no-cache musl-dev gcc openssl-dev
WORKDIR /app
# Copy the Cargo.toml file to build the dependency cache
COPY Cargo.toml ./
# Now copy the actual source code
COPY ./src ./src
# Build the Rust application in release mode
RUN cargo build --release
# Stage 2: Create a minimal image
FROM alpine:3.20
# Copy the compiled binary from the builder stage
COPY --from=builder /app/target/release/file_organizer_cli_tool_rust /usr/local/bin/file_organizer
# Expose the desired port
EXPOSE 8082
# Set the entry point for the container
ENTRYPOINT ["file_organizer_cli_tool_rust"]
Step 7: Build the Docker Image
To run your Docker image. Execute the command from the root directory of your project or the same path where your Dockerfile
resides.
docker build -t file_organizer_cli_tool_rust:v0.0.2 .
Step 8: Running the Docker Container
You can run your Docker container by specifying the directory you want to organize. Make sure you mount the directory
to the container, the directory
will contain the files you want to organize. See below the code snippet. Where
DATA_DIR
the files to be organized reside. ReplaceDATA_DIR
with the location of your directory.file_organizer_cli_tool_rust
The Docker container we recently built contains the Rust code.
Below is the screenshot of the files before running the CLI Program
Below is the screenshot of the files after running the CLI program
Below is the command executed
export DATA_DIR=~/Documents/file_organizer_test
docker run -p 8082:8082 --rm -v "$DATA_DIR":/data file_organizer_cli_tool_rust:v0.0.2 /data
From the screenshot shared, you can see that the files have been successfully organized into the corresponding subdirectory of the file extensions.
Conclusion
In this article, we walked through the process of building a file organizer CLI tool using Rust. We covered the process of setting up a Rust project, implemented the core functionality and dockerized the application. This project is a great way of getting hands-on experience working with Rust and learning how to create a CLI tool. I do hope you find the article interesting. Please feel free to check the full code here https://github.com/ExitoLab/file_organizer_cli_tool_rust