Summary - CLI with clap
- Rust course
- Intermediate
- Lesson 100/115
- 5 min
Combine derive, flag and subcommand to build a file CLI.
CLI with clap summary
In clap's derive interface, structs are the argument layout: #[derive(Parser)], and field types decide the conversion. #[arg(short, long)] gives flag names, default_value_t the default; bool is a valueless flag, Option<T> optional, Vec<T> multiple positional. Subcommands are #[derive(Subcommand)] enums. Help, version and error messages are generated automatically.
Global flag and subcommand
Top-level options and the subcommand form one structure together:
#[derive(Debug)]
struct Cli {
sessiz: bool,
komut: Komut,
}
fn cli_ayristir(args: &[&str]) -> Result<Cli, String> {
let sessiz = args.contains(&"--sessiz");
let kalan: Vec<&str> = args.iter().copied().filter(|a| *a != "--sessiz").collect();
Ok(Cli { sessiz, komut: komut_ayristir(&kalan)? })
}Practice Full CLI
Write the Cli and cli_ayristir code above together with Komut and komut_ayristir from the previous lesson. Print the result of ["--sessiz", "sil", "2"] with {:?}.
Starter code
#[derive(Debug)]
enum Komut {
Ekle { metin: String },
Liste,
Sil { no: usize },
}
fn komut_ayristir(args: &[&str]) -> Result<Komut, String> {
match args {
["ekle", metin @ ..] if !metin.is_empty() => Ok(Komut::Ekle { metin: metin.join(" ") }),
["liste"] => Ok(Komut::Liste),
["sil", n] => n
.parse()
.map(|no| Komut::Sil { no })
.map_err(|_| format!("geçersiz numara: {n}")),
[k, ..] => Err(format!("bilinmeyen alt komut: {k}")),
[] => Err(String::from("alt komut gerekli")),
}
}
fn main() {
// buraya yaz
}Sample solution
#[derive(Debug)]
enum Komut {
Ekle { metin: String },
Liste,
Sil { no: usize },
}
fn komut_ayristir(args: &[&str]) -> Result<Komut, String> {
match args {
["ekle", metin @ ..] if !metin.is_empty() => Ok(Komut::Ekle { metin: metin.join(" ") }),
["liste"] => Ok(Komut::Liste),
["sil", n] => n
.parse()
.map(|no| Komut::Sil { no })
.map_err(|_| format!("geçersiz numara: {n}")),
[k, ..] => Err(format!("bilinmeyen alt komut: {k}")),
[] => Err(String::from("alt komut gerekli")),
}
}
#[derive(Debug)]
struct Cli {
sessiz: bool,
komut: Komut,
}
fn cli_ayristir(args: &[&str]) -> Result<Cli, String> {
let sessiz = args.contains(&"--sessiz");
let kalan: Vec<&str> = args.iter().copied().filter(|a| *a != "--sessiz").collect();
Ok(Cli {
sessiz,
komut: komut_ayristir(&kalan)?,
})
}
fn main() {
println!("{:?}", cli_ayristir(&["--sessiz", "sil", "2"]));
}Expected output
Ok(Cli { sessiz: true, komut: Sil { no: 2 } })
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 Where does a <code>--sessiz</code> flag valid for all subcommands go?
- In the top-level Cli struct (with #[arg(global = true)]) Correct answer
- In each variant separately
- In a static outside the Komut enum
- It can't be
Explanation: global = true lets the flag be written before or after the subcommand.
2 Derive subcommands
#[derive(____)]
enum Komut {
Ekle { metin: String },
Liste,
}
#[derive(Subcommand)]
enum Komut {
Ekle { metin: String },
Liste,
}
Explanation: Subcommand enums derive Subcommand.
3 Group summary
| Term | Meaning |
|---|---|
#[derive(Parser)] |
Argument layout |
bool alan |
Valueless flag |
Option<T> alan |
Optional |
#[derive(Subcommand)] |
Subcommands |