Rust
Summary - CLI with clap
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)? })
}
Exercise 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 {:?}.
Show example 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"]));
}
Test yourself
-
Where does a <code>--sessiz</code> flag valid for all subcommands go?
- In the top-level Cli struct (with #[arg(global = true)])
- In each variant separately
- In a static outside the Komut enum
- It can't be
Show answer
In the top-level Cli struct (with #[arg(global = true)]) — global = true lets the flag be written before or after the subcommand.
-
Derive subcommands
#[derive(____)]
enum Komut {
Ekle { metin: String },
Liste,
}
Show answer
#[derive(Subcommand)]
enum Komut {
Ekle { metin: String },
Liste,
}
-
Group summary
- #[derive(Parser)]
- Argument layout
- bool alan
- Valueless flag
- Option<T> alan
- Optional
- #[derive(Subcommand)]
- Subcommands
Next lesson: Serialize and Deserialize