In this lab you will design a multi-subcommand command-line tool using clap's derive API, wire each subcommand to a SQLite database layer, and configure the database path via flags, environment variables, and defaults.
By the end of this lab, you will be able to:
- Define a CLI with subcommands using
#[derive(Parser)]and#[derive(Subcommand)] - Accept flags, positional arguments, and environment variables with
#[arg(...)] - Dispatch subcommands in a
matchblock - Separate CLI parsing from database logic
- Completed Lab 6
- Add
clap = { version = "4", features = ["derive"] }toCargo.toml
clapderive API: Annotated structs and enums generate the full argument parser at compile time#[command(subcommand)]: Marks a field as the active subcommand#[arg(env = "VAR")]: Reads the value from an environment variable when the flag is absent- Separation of concerns: Parse args in
main, pass the connection to domain functions
use clap::Parser;
use std::path::PathBuf;
#[derive(Parser)]
#[command(name = "mydb", about = "A simple SQLite CLI")]
struct Cli {
#[arg(long, env = "MYDB_PATH", default_value = "mydb.sqlite")]
db: PathBuf,
#[command(subcommand)]
command: Commands,
}use clap::Subcommand;
#[derive(Subcommand)]
enum Commands {
/// Initialize the database schema
Init,
/// Insert a key-value pair
Set { key: String, value: String },
/// Retrieve a value by key
Get { key: String },
}fn main() -> rusqlite::Result<()> {
let cli = Cli::parse();
let conn = rusqlite::Connection::open(&cli.db)?;
match cli.command {
Commands::Init => init(&conn)?,
Commands::Set { key, value } => set(&conn, &key, &value)?,
Commands::Get { key } => get(&conn, &key)?,
}
Ok(())
}# Init schema
cargo run -p cli-architecture -- init
# Set a value
cargo run -p cli-architecture -- set greeting "Hello, world"
# Get a value
cargo run -p cli-architecture -- get greeting
# Use a custom DB path via env var
MYDB_PATH=/tmp/test.db cargo run -p cli-architecture -- initcargo run -p cli-architecture -- --help
cargo run -p cli-architecture -- set --helpcargo run -p cli-architecture- Add a
delete <key>subcommand that removes an entry from the database. - Add a
listsubcommand that prints all key-value pairs formatted askey = value. - Add a
--verboseglobal flag that prints the SQL query being executed before each operation.
In this lab, you learned how to:
- Use
clap's derive API for zero-boilerplate CLI parsing - Define subcommands with per-command arguments
- Read configuration from flags, env vars, and defaults
- Dispatch subcommands with a
matchblock
Continue to Lab 8: Crawling the Filesystem and Persisting Metadata.