Introduction
In this little post, we'll build together a very simple command line application using Rust. The goal of the program will be straightforward. We'll give it a directory name, and it will create a directory with that name. Easy enough right? Let's jump into it.
Setup
If you haven't set up Rust yet, do not hesitate to check out the official docs, there are a lot of great informations there.
Now, let's create our new Rust project. To do this, we can run the following command:
cargo new new_dir_cmd
This will create a new directory call new_dir_cmd. Of course, you can replace that name with whatever you want.
If you peek inside the newly created directory, you'll see the following:
Out of the box, if you run cargo run
inside this directory. It will print out the good old "Hello World!"
Awesome, we're ready to go!
Creating a folder
First step, we're going to create a folder. We will improve our program step by step.
Let's put this inside our main.rs:
use std::fs;
fn main() -> std::io::Result<()> {
fs::create_dir_all("/Users/damiencosset/Desktop/rust/new_dir_cmd/awesome_new_dir")?;
Ok(())
}
We use the fs module from the std crate. Crates, in Rust, are similar to packages in other languages. If you want more informations about crates in Rust, you can check the docs.
We use the create_dir_all function from the module. It takes a path as an argument. For now, I'm giving it a hardcoded path.
Finally, this function returns a std::io::Result type. So we make our main function return that by adding -> std::io::Result<()>. After creating our new directory, we simply use the Ok variant that indicates that everything is fine.
Note: The ? character is used in Rust for error propagation. If we encounter a error during the directory creation it would return an Err(error), Err being the second variant that Result can take after Ok.
Let's run this code and check what we have:
It works! We have created our first new directory with Rust!
Adding the command line arguments
Obviously, this program isn't very useful. The goal is to give the directory name we want to create via a command line. Let's set this up.
Adding the clap crate
To parse command line arguments, we will use the clap crate. To add a crate, we go into our Cargo.toml file. You have a line that says:
[dependencies]
This is where we add the crates we want to use in our program.
Let's add clap by running the following command:
cargo add clap --features derive
The features flag allows us to use optional features in the crate. Here, we want to use the derive feature. To get more information about this crate, you can check the docs. And if you check our Cargo.toml, we now have a new line:
[dependencies]
clap = { version = "4.5.4", features = ["derive"] }
Defining our command line arguments
Let's replace our existing code with this:
use clap::Parser;
//use std::fs;
#[derive(Parser)]
struct Cli {
directory_name: String,
}
fn main() -> std::io::Result<()> {
let args = Cli::parse();
println!("{}", args.directory_name);
// fs::create_dir_all("/Users/damiencosset/Desktop/rust/new_dir_cmd/awesome_new_dir")?;
Ok(())
}
We commented out the directory creation for now.
#[derive(Parser)]
means that we are using a custom macro that implements the Parser trait. Simply put, a trait defines a set of methods. Here, we implement the set of of methods defined in Parser to our Cli structure. So our structure is able to call said methods, like parse().
With this code right there and the power of clap, we can parse command line applications. Let's run our code and give it an argument:
Nice! We successfully printed our argument. We can now parse the directory name from the command line.
Getting the current directory
Now, we have a little issue. We are not going to give the full path in our argument. We only give the directory name we wish to create. Therefore, our program needs to know where to create this directory. We'll do something simple, we'll take the current directory we are in, and append the directory name we put as an argument.
So we need to:
- get the current directory's path
- append the directory name to that path
To get the current directory's path, we can add these two lines of code:
// Import
use std::env;
// Inside the main function
let mut path = env::current_dir()?;
Notice that we use the keyword mut. This means that the variable we just defined is mutable. Variables in Rust are by default immutable. By adding the keyword mut, we can modify it to append the directory name we wish to create.
By combining all of this, our completed program looks like this:
use clap::Parser;
use std::env;
use std::fs;
#[derive(Parser)]
struct Cli {
directory_name: String,
}
fn main() -> std::io::Result<()> {
let args = Cli::parse();
let mut path = env::current_dir()?;
path.push(args.directory_name);
fs::create_dir_all(path)?;
Ok(())
}
By using push(), we can append our directory name to the current directory's path. Let's see if it works!
Yay! It works!
And just like that, we build a very simple program in Rust that allows us to create new directories!
Hope you found it interesting!
Have fun ❤
Top comments (0)