Week 14: CLI applications
Session 1: CLI architecture and 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 -- builds and starts the Week 14 binary 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().
Try it yourself: Run in Rust Playground
Why is this bad? - Manual string parsing is error-prone. - Type conversion (String to integer) is tedious. - Generating --help menus manually is a nightmare.
clapclap (Command Line Argument Parser) is a widely used crate for building CLIs in Rust.
Features: - Declarative macro-based or derive-based API - Automatic --help and --version generation - Type parsing and configurable value validation - Subcommand routing
The course uses clap’s derive API so the command structure is visible in Rust structs and enums.
clapFirst, add it to your Cargo.toml. The derive feature enables the macro API used in these examples.
clap using a structWe 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 using an enumFor 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, use match to route each subcommand:
Building a CLI application isn’t just about parsing arguments. It requires pulling together everything we’ve learned:
mod) to separate the interface from logic.rand for random generation.Keep main.rs focused on parsing and routing; place password generation and validation in the supplied modules so they can be tested independently.
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 complete a 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.
A green Week 14 badge represents the complete 10-point lab.
Instructions: week14/lab14.md
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.
You have moved from Python’s dynamic runtime model to Rust’s compiled type and ownership systems.
You now have enough vocabulary to compare the trade-offs and continue learning either language.
Lab 14 is the last graded lab. What remains is the final project.
Optional further reading: the advanced AI workflows appendix collects prompting, review, and verification habits worth carrying into the project.
IS 4010: App Dev with AI