How To Build CLI App In Rust Using Clap - Part 2

How To Build CLI App In Rust Using Clap - Part 2

Generate Command

Previously, we ran our first iteration of the cli application by setting up the project and adding commands. In this post, we will cover our first command, generate, that generates a secret containing the key and the IV (initial-value) to the output file given as a flag for this command.

Implementing Generate Command Logic

Create a new file generate.rs and start adding logic to generate a key and an IV (initial-value). Install a new dependency for generating random values by running the below command.

cargo add rand

Screenshot 2022-09-28 at 2.42.30 PM.png

Import newly added dependency to main.rs file. And import generate.rs in main.rs.

Let us add logic to the generate.rs file:

Here, we are using a crate called serde for serialization and deserialization. Add serde dependencies to Cargo.toml file.

serde_json = "1.0.85"
serde = "1.0.85"
serde_derive = "1.0.85"

More on serde

Define a struct Secret with two members key and initial-value both are of type String.

Now implement a function new for Secret struct. This function is used to generate a Secret by encoding the key and initial-value to base64.

Using rand crate a key and an initial value are generated from random values. Both key and initial-value are of length 16. Then the secret is written to the output file path which is received as user input.

use super::*;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Secret {
    key: String,
    inital_value: String,
}

impl Secret {
    pub fn new(out_path: String) {
        let mut key = [0u8; 16];
        thread_rng().fill_bytes(&mut key[..]);

        let mut inital_value = [0u8; 16];
        thread_rng().fill_bytes(&mut inital_value[..]);

        let secret = Secret {
            key: base64::encode(key),
            inital_value: base64::encode(inital_value),
        };

        let secret = to_vec(&secret).unwrap();
        let mut file = File::create(out_path).unwrap();
        file.write_all(&secret).unwrap();

        println!("Secret Generated Successfully")
    }

Fixing up main.rs file to generate the secret Keystore.

use clap::{Parser, Subcommand};
use generate::Secret;
use rand::*;
mod commands;
use commands::Cryptifer;
mod generate;
use serde_derive::{Deserialize, Serialize};
use serde_json::to_vec;
use std::fs::File;
use std::io::Write;


fn main() {
    let cryptifer = Cryptifer::parse();

    match cryptifer.command {
        commands::Commands::Generate { output_path } => {
            Secret::new(output_path);
        }
        commands::Commands::Encrypt {
            file_path: _,
            key_path: _,
        } => todo!(),
        commands::Commands::Decrypt {
            encrypted_file: _,
            key_path: _,
        } => todo!(),
    }
}

Running Your First Command

cargo run generate -o Keystore.secret

Screenshot 2022-09-28 at 6.24.20 PM.png

That generates the secret file with the key and Initial-value. Screenshot 2022-09-28 at 6.27.30 PM.png

Wrapping Things Up

In this post, we achieved our milestone by completing the first command of our CLI application. This command generates key and initial-value from the random values. Then we encoded those values into base64 and stored them in a file. If you run into any issues with any part of this tutorial, please leave a comment so that I can update the content to be more clear. If you like this post, please follow me on Hashnode and subscribe to my newsletter for future updates.

Did you find this article valuable?

Support The Missing Semicolon by becoming a sponsor. Any amount is appreciated!