rust
52 lines · 10 steps
Building a CLI with clap's derive macros
clap turns annotated Rust structs and enums into a full command-line parser with subcommands, validation, and defaults.
Explained by
highlit
1use std::path::PathBuf;
2
3use clap::{Parser, Subcommand, ValueEnum};
4
5#[derive(Parser)]
6#[command(name = "imgtool", version, about = "Batch image processor")]
7struct Cli {
8 #[arg(short, long, global = true, action = clap::ArgAction::Count)]
9 verbose: u8,
10
11 #[arg(long, value_name = "FILE", global = true)]
12 config: Option<PathBuf>,
13
14 #[command(subcommand)]
15 command: Command,
16}
17
18#[derive(Subcommand)]
19enum Command {
20 Convert {
21 #[arg(required = true)]
22 inputs: Vec<PathBuf>,
23
24 #[arg(short, long)]
25 output_dir: PathBuf,
26
27 #[arg(short, long, value_enum, default_value_t = Format::Png)]
28 format: Format,
29
30 #[arg(short, long, value_parser = clap::value_parser!(u8).range(1..=100), default_value_t = 85)]
31 quality: u8,
32 },
33 Resize {
34 input: PathBuf,
35
36 #[arg(short = 'W', long)]
37 width: Option<u32>,
38
39 #[arg(short = 'H', long)]
40 height: Option<u32>,
41
42 #[arg(long, conflicts_with_all = ["width", "height"])]
43 scale: Option<f32>,
44 },
45}
46
47#[derive(Copy, Clone, ValueEnum)]
48enum Format {
49 Png,
50 Jpeg,
51 Webp,
52}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Deriving Parser and Subcommand lets clap generate parsing, help, and validation from your type definitions.
- 2Attribute options like global, conflicts_with_all, and value_parser encode CLI rules declaratively instead of hand-written checks.
- 3Modeling optional flags as Option and choices as ValueEnum makes invalid states hard to represent.
Related explainers
rust
use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(untagged)]
Parsing flexible JSON shapes with serde
deserialization
enums
json
Intermediate
6 steps
php
<?php namespace App\Services\Checkout;
Validating coupons with Laravel's Pipeline
pipeline
chain of responsibility
transactions
Intermediate
7 steps
rust
use std::cmp::{Ordering, Reverse}; use std::collections::BinaryHeap; use std::fs::File; use std::io::{self, BufRead, BufReader, BufWriter, Lines, Write};
K-way merge of sorted logs in Rust
binary-heap
k-way-merge
streaming-io
Intermediate
8 steps
php
<?php namespace App\Services;
How a password strength validator works in PHP
validation
regular-expressions
data-driven
Intermediate
8 steps
rust
use axum::{ extract::{Path, State}, response::sse::{Event, KeepAlive, Sse}, };
Streaming import progress with SSE in Axum
server-sent-events
streams
watch-channel
Advanced
7 steps
rust
use std::f64::consts::PI; #[derive(Debug, Clone, Copy)] pub struct LatLng {
Building geographic bounding boxes in Rust
geospatial
value-types
option
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/building-a-cli-with-clap-s-derive-macros-explained-rust-4e73/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.