Week 14: CLI Applications
Session 1: CLI Architecture & clap - The anatomy of a CLI tool - Argument parsing with the clap crate - Subcommands and flags
Session 2: Capstone Integration - Managing external crates (rand) - Structuring larger projects (modules) - Synthesizing traits, ownership, and error handling
clapWhile graphical interfaces (GUIs) are great for end-users, command-line interfaces (CLIs) remain the backbone of developer tooling.
Why build CLI tools? - Automation: Easily scriptable in CI/CD pipelines - Speed: Faster for power users to execute complex commands - Composability: Can be piped together (grep | wc -l) - Low Overhead: Minimal system resources required
Think about the tools you use: git, python, cargo, pytest.
Let’s break this down: 1. Executable: cargo run --bin password_gen -- (The program itself) 2. Subcommand: random (A major action the program can take) 3. Option / Argument: --length 16 (A named parameter with a value) 4. Flag: --symbols (A boolean toggle, no value needed)
You could parse arguments manually using std::env::args().
Why is this bad? - Manual string parsing is error-prone. - Type conversion (String to integer) is tedious. - Generating --help menus manually is a nightmare.
clap 👏clap (Command Line Argument Parser) is the industry standard crate for building CLIs in Rust.
Features: - Declarative macro-based or derive-based API - Automatic --help and --version generation - Built-in type validation and bounds checking - Subcommand routing
Fun Fact: The Cargo CLI itself uses clap under the hood!
clapFirst, add it to your Cargo.toml. We enable the derive feature to use Rust’s powerful attribute macros.
clap (Struct)We define our expected arguments as a standard Rust struct.
use clap::Parser;
/// A simple password generator
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Cli {
/// Length of the password
#[arg(short, long, default_value_t = 12)]
length: u8,
/// Include special symbols
#[arg(short, long)]
symbols: bool,
}
fn main() {
let cli = Cli::parse();
println!("Length: {}, Symbols: {}", cli.length, cli.symbols);
}clap (Enum)For complex tools (like git or cargo), you use subcommands. We model these using an enum.
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(version, about)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Generate a random password
Random {
#[arg(short, long, default_value_t = 12)]
length: u8,
},
/// Validate an existing password
Validate {
password: String,
},
}Once parsed, we use Rust’s powerful match statement to route the logic:
Building a CLI application isn’t just about parsing arguments. It requires pulling together everything we’ve learned:
mod) to separate UI from logic.rand for secure number generation.Don’t put everything in main.rs. A professional CLI tool separates the argument parsing (the interface) from the actual business logic.
src/
├── main.rs # CLI definition and command routing
├── generator.rs # Password generation logic
└── validator.rs # Password strength calculation
In main.rs:
rand CrateRandomness is not built into the Rust standard library (unlike Python’s import random). You must use an external crate.
To generate a password, we often need to select random characters from a slice.
use rand::Rng;
use rand::seq::SliceRandom;
fn main() {
let charset: &[u8] = b"abcdefghijklmnopqrstuvwxyz0123456789";
let mut rng = rand::thread_rng();
// Pick a random byte from the slice and convert to char
let random_char = *charset.choose(&mut rng).unwrap() as char;
println!("Random char: {}", random_char);
}For your final lab, you will build a complete Password Utility CLI.
Features to implement: 1. random: Generate passwords from character sets. 2. passphrase: Generate correct-horse-battery-staple style passphrases. 3. pin: Generate numeric PINs. 4. validate: Calculate entropy and check for common patterns in existing passwords.
You will implement the logic in generator.rs and validator.rs, and wire it up to the clap parser in main.rs.
As you tackle the capstone, remember your AI Copilot strategies:
fn generate_pin(length: usize) -> String. I have the rand crate installed. Explain the approach using a for loop before showing any code.”Use AI to help you understand compiler errors, explore the clap documentation, and suggest idiomatic iterator patterns.
Congratulations! You’ve traversed the steepest learning curve in modern programming.
You’ve moved from Python’s dynamic, interpreted flexibility to Rust’s compiled, type-safe, and blazingly fast memory safety.
You now have the vocabulary and experience to choose the right tool for the job.
Next week: Advanced AI Workflows.
IS4010: App Development with AI