henriquehbr.xyz

Blog Supporters Resume
Part time open-sourcerer, Linux enthusiast, Rust fanatic

Creating a simple CLI in Rust

September 12, 2021

Summary

Setup

First of all, you must have rustup installed, in order to do so, run the following command:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

This is the official way to install rustup as recommended on rustup.rs

It will fetch the shell installation script, and execute it right away, without saving anything on your machine, when executed, the script will instruct you through the whole process with well explained and detailed installation instructions, carefully follow them until the whole process is done

Initializing a new project

After that, move to a directory of your preference (optional) and initialize a new empty project by running:

cargo new <project_name>

A directory with the specified project name will be created on the current directory, besides that, a little few others things will be added under the hood, it’s structure should look like the following:

.
├── Cargo.toml
└── src
    └── main.rs

Creating the CLI

Differently from Node, we don’t have a CLI for installing packages automatically for us, instead, is necessary to manually write the desired package name and version directly on Cargo.toml

Tip: you can leverage this by using a plugin for your editor of choice, for instance, in my case, i’m using neovim, so something like vim-crates can be really useful when it comes to updating dependencies

For creating ou CLI, we need the structopt dependency, it’s responsible for generating a command line parser, based on a given struct, to install it, simply include the following line on your Cargo.toml, more specifically, under the [dependencies] table:

structopt = "0.3.23"

You don’t necessarily need to use this version, it’s the latest at the time of writing, but feel free to use the latest one available to you (unless there are breaking dependencies, then probably won’t work)

Now, the most important part, we need to establish the logic of our program, for this example, it will have a required name argument, and a optional --is-programmer flag, our CLI will greet the user by output “Hey <user>!”, and if the --is-programmer flag is passed, it will append “Have you heard about Rust?” to the greeting message:

// imports a external dependency, don't confuse it with `mod`
// which is used for importing local modules
use structopt::StructOpt;

// pass the struct below as an argument to `StructOpt` for generating the
// command line parser
#[derive(StructOpt)]
struct Cli {
    // required argument, represents the person name
    name: String,

    // optional flag, will define whether or not the second part of the
    // greeting will be displayed short flag will be set to "-p", long flag
    // will be determined accordingly to the property name "--is-programmer"
    #[structopt(short = "p", long)]
    is_programmer: bool
}

fn main() {
    // gets a object containing all the CLI flags and their respective values
    let args = Cli::from_args();

    // the message to be displayed to the user
    let mut message_vector = vec!["Hey ", &args.name, "! "];

    // complements the greeting if the user is a programmer
    if args.is_programmer {
        message_vector.push("Have you heard about Rust?")
    }

    // concatenate the message vector into a string
    let message = message_vector.join("");

    // displays the greeting message
    println!("{}", message.join)
}

Now, let’s test it with cargo run and pass some flags to see if everything is working properly:

cargo run Edgar

And we should expect the following output:

Hey Edgar!

And with the --is-programmer flag:

cargo run -- --is-programmer Daisy

The output below should be displayed:

Hey Daisy! Have you heard about Rust?

That’s all for this post, consider reading structopt docs for more information on this amazing library!