21 KiB
clap — krok za krokom
Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej.
Cargo.toml:
[dependencies]
clap = { version = "4", features = ["derive"] }
use clap::Parser;
Kapitola 1: Čo je clap
clap je knižnica na spracovanie argumentov príkazového riadku (CLI argumentov).
Keď spustíš program z terminálu:
./moj_program --subor data.json --pocet 5 --verbose
clap ti tie argumenty automaticky sparsuje do Rust štruktúry.
use clap::Parser;
#[derive(Parser)]
struct Argumenty {
#[arg(short, long)]
subor: String,
#[arg(short, long)]
pocet: u32,
#[arg(short, long)]
verbose: bool,
}
fn main() {
let args = Argumenty::parse();
println!("Súbor: {}", args.subor);
println!("Počet: {}", args.pocet);
println!("Verbose: {}", args.verbose);
}
Spustenie:
./moj_program --subor data.json --pocet 5 --verbose
# Súbor: data.json
# Počet: 5
# Verbose: true
./moj_program -s data.json -p 5 -v
# To isté — short varianty
./moj_program --help
# Automaticky vygenerovaná nápoveda!
Čo clap robí za teba:
- Parsuje argumenty z príkazového riadku
- Validuje typy (ak --pocet nie je číslo → chybová hláška)
- Generuje
--helpautomaticky - Generuje chybové hlášky ak niečo chýba
- Podporuje short (
-s) aj long (--subor) verzie
Úlohy 1
1a. Čo je CLI argument? Uveď 3 príklady programov, ktoré berú argumenty (napr. ls -la).
1b. Napíš štruktúru Args s jedným argumentom meno: String (long + short). Sparsuj a vypíš.
1c. Čo sa stane keď spustíš program bez argumentov? Čo keď s --help?
Kapitola 2: Typy argumentov
Povinný argument
#[derive(Parser)]
struct Args {
#[arg(short, long)]
subor: String, // POVINNÝ — program spadne ak chýba
}
./program --subor data.json # OK
./program # CHYBA: "required arguments were not provided"
Voliteľný argument (Option)
#[derive(Parser)]
struct Args {
#[arg(short, long)]
subor: Option<String>, // VOLITEĽNÝ — None ak neuvedený
}
./program --subor data.json # args.subor = Some("data.json")
./program # args.subor = None
Bool flag
#[derive(Parser)]
struct Args {
#[arg(short, long)]
verbose: bool, // false ak neuvedený, true ak uvedený
}
./program --verbose # args.verbose = true
./program -v # args.verbose = true
./program # args.verbose = false
Bool flag neberá hodnotu! Stačí ho uviesť a je true.
Argument s default hodnotou
#[derive(Parser)]
struct Args {
#[arg(short, long, default_value = "data.json")]
subor: String,
#[arg(short, long, default_value_t = 10)]
pocet: u32,
}
./program # subor = "data.json", pocet = 10
./program --subor iny.json # subor = "iny.json", pocet = 10
./program -p 5 # subor = "data.json", pocet = 5
Pozor: default_value je pre String (vždy v úvodzovkách). default_value_t je pre iné typy (čísla, bool).
Úlohy 2
2a. Napíš Args s:
subor: String— povinnývystup: Option<String>— voliteľnýpocet: u32— default 5verbose: bool— flag
Vyskúšaj rôzne kombinácie spustenia.
2b. Čo sa stane ak zadáš --pocet ahoj (nie číslo)?
2c. Čo je rozdiel medzi Option<String> a String s default_value?
Kapitola 3: short a long
Automatické short a long
#[derive(Parser)]
struct Args {
#[arg(short, long)]
subor: String,
// --subor hodnota (long = názov poľa)
// -s hodnota (short = prvé písmeno)
}
Len long (bez short)
#[derive(Parser)]
struct Args {
#[arg(long)]
konfig: String,
// --konfig hodnota OK
// -k hodnota NEFUNGUJE
}
Len short (bez long)
#[derive(Parser)]
struct Args {
#[arg(short)]
verbose: bool,
// -v OK
// --verbose NEFUNGUJE
}
Vlastné short/long
#[derive(Parser)]
struct Args {
#[arg(short = 'f', long = "file")]
subor: String,
// -f hodnota OK
// --file hodnota OK
// --subor hodnota NEFUNGUJE (premenovali sme)
}
Kolízia short písmen
// PROBLÉM: obe majú short 's'
#[derive(Parser)]
struct Args {
#[arg(short, long)]
subor: String, // -s
#[arg(short, long)]
stav: String, // -s KOLÍZIA!
}
// Riešenie: jedného premenuj
#[derive(Parser)]
struct Args {
#[arg(short, long)]
subor: String, // -s
#[arg(short = 't', long)]
stav: String, // -t
}
Úlohy 3
3a. Napíš Args kde:
inputmá short-ia long--inputoutputmá short-oa long--outputdebugmá len short-d(žiadny long)formatmá len long--format(žiadny short)
3b. Máš 3 polia: subor, stav, slovo. Všetky začínajú na 's'. Vyrieš kolíziu short písmen.
Kapitola 4: Pozičné argumenty
Doteraz sme používali pomenované argumenty (--subor, -s). Existujú aj pozičné — bez mena, len podľa poradia.
#[derive(Parser)]
struct Args {
/// Vstupný súbor
vstup: String, // PRVÝ pozičný argument
/// Výstupný súbor
vystup: String, // DRUHÝ pozičný argument
#[arg(short, long)]
verbose: bool, // Pomenovaný (flag)
}
./program input.json output.json --verbose
# ^^^^^^^^^^ ^^^^^^^^^^^
# prvý druhý pozičný
Pravidlo: Ak pole nemá #[arg(short, long)], je pozičné.
Voliteľný pozičný
#[derive(Parser)]
struct Args {
vstup: String, // povinný pozičný
vystup: Option<String>, // voliteľný pozičný
}
./program input.json # vystup = None
./program input.json output.json # vystup = Some("output.json")
Pozičný s default
#[derive(Parser)]
struct Args {
#[arg(default_value = "data.json")]
subor: String,
}
./program # subor = "data.json"
./program iny.json # subor = "iny.json"
Úlohy 4
4a. Napíš Args pre program, ktorý kopíruje súbor:
./kopiruj zdrojovy.txt cielovy.txt
Dva pozičné argumenty.
4b. Napíš Args pre grep-like program:
./hladaj "hľadaný text" subor.txt --ignore-case
Pozičné: vzor a súbor. Pomenované: ignore_case (bool flag).
Kapitola 5: Popis a help text
Dokumentačné komentáre → help text
/// Správca knižnice — CLI nástroj na správu kníh
#[derive(Parser)]
struct Args {
/// Cesta k JSON súboru s dátami
#[arg(short, long)]
subor: String,
/// Počet výsledkov na stránku
#[arg(short, long, default_value_t = 10)]
pocet: u32,
/// Zapni podrobný výpis
#[arg(short, long)]
verbose: bool,
}
./program --help
# Správca knižnice — CLI nástroj na správu kníh
#
# Usage: program [OPTIONS] --subor <SUBOR>
#
# Options:
# -s, --subor <SUBOR> Cesta k JSON súboru s dátami
# -p, --pocet <POCET> Počet výsledkov na stránku [default: 10]
# -v, --verbose Zapni podrobný výpis
# -h, --help Print help
Pravidlo: /// komentár nad štruktúrou = popis programu. /// nad poľom = popis argumentu. clap ich automaticky zobrazí v --help.
about a help atribúty
#[derive(Parser)]
#[command(about = "Správca knižnice", version = "1.0")]
struct Args {
#[arg(short, long, help = "Cesta k súboru")]
subor: String,
}
./program --version
# program 1.0
Úlohy 5
5a. Pridaj ku každému argumentu z úlohy 2a popis (/// komentár). Spusti s --help a pozri výstup.
5b. Pridaj k štruktúre #[command(about, version)]. Vyskúšaj --help a --version.
Kapitola 6: Subcommands — podpríkazy
Väčšie programy majú podpríkazy:
git add file.txt
git commit -m "správa"
git push origin main
V clap to riešiš cez enum:
use clap::{Parser, Subcommand};
#[derive(Parser)]
struct Args {
#[command(subcommand)]
prikaz: Prikaz,
}
#[derive(Subcommand)]
enum Prikaz {
/// Pridaj novú knihu
Pridaj {
/// Názov knihy
nazov: String,
/// Autor knihy
autor: String,
},
/// Odstráň knihu podľa názvu
Odstran {
/// Názov knihy na odstránenie
nazov: String,
},
/// Vypíš všetky knihy
Vypis,
/// Hľadaj knihy
Hladaj {
/// Hľadaný výraz
vyraz: String,
},
}
fn main() {
let args = Args::parse();
match args.prikaz {
Prikaz::Pridaj { nazov, autor } => {
println!("Pridávam: {} od {}", nazov, autor);
}
Prikaz::Odstran { nazov } => {
println!("Odstraňujem: {}", nazov);
}
Prikaz::Vypis => {
println!("Výpis kníh...");
}
Prikaz::Hladaj { vyraz } => {
println!("Hľadám: {}", vyraz);
}
}
}
./program pridaj "Duna" "Frank Herbert"
./program odstran "Duna"
./program vypis
./program hladaj "Herbert"
./program --help
# Shows all subcommands
./program pridaj --help
# Shows help for "pridaj" subcommand
Spoločné argumenty + subcommand
#[derive(Parser)]
struct Args {
/// Cesta k JSON súboru
#[arg(short, long, default_value = "data.json")]
subor: String,
#[command(subcommand)]
prikaz: Prikaz,
}
./program --subor moje.json pridaj "Duna" "Herbert"
# ^^^^^^^^^^^^^^^^^ ^^^^^^ spoločný arg pred subcommand
Úlohy 6
6a. Napíš CLI pre správcu kontaktov:
./kontakty pridaj "Anna" "+421900000000"
./kontakty odstran "Anna"
./kontakty vypis
./kontakty hladaj "Ann"
6b. Pridaj spoločný argument --subor (default "kontakty.json") pred subcommand.
6c. Pridaj do pridaj subcomandu voliteľný argument --email.
Kapitola 7: ValueEnum — enum ako argument
Ak chceš aby argument bol jedna z preddefinovaných hodnôt:
use clap::{Parser, ValueEnum};
#[derive(Clone, ValueEnum)]
enum Format {
Json,
Csv,
Text,
}
#[derive(Parser)]
struct Args {
#[arg(short, long, default_value = "json")]
format: Format,
}
fn main() {
let args = Args::parse();
match args.format {
Format::Json => println!("Výstup ako JSON"),
Format::Csv => println!("Výstup ako CSV"),
Format::Text => println!("Výstup ako text"),
}
}
./program --format json
./program --format csv
./program --format text
./program --format xml # CHYBA: "invalid value 'xml'"
./program --help
# -f, --format <FORMAT> [default: json] [possible values: json, csv, text]
clap automaticky:
- Ukáže povolené hodnoty v
--help - Odmietne neplatnú hodnotu
- Case-insensitive porovnanie
Na skúške — enum Stav ako argument
#[derive(Clone, ValueEnum, Serialize, Deserialize, PartialEq, Debug)]
enum Stav {
Nova,
Pouzivana,
Poskodena,
Vyradena,
}
#[derive(Subcommand)]
enum Prikaz {
ZmenStav {
nazov: String,
#[arg(short, long)]
stav: Stav,
},
PodlaStavu {
#[arg(short, long)]
stav: Stav,
},
}
./program zmen-stav "Duna" --stav pouzivana
./program podla-stavu --stav nova
Úlohy 7
7a. Vytvor enum Priorita { Nizka, Stredna, Vysoka } s ValueEnum. Použi ho ako argument v CLI.
7b. Pridaj do subcommandu pridaj argument --priorita typu Priorita s defaultom Stredna.
Kapitola 8: Vec argumenty — viacero hodnôt
#[derive(Parser)]
struct Args {
/// Súbory na spracovanie
subory: Vec<String>, // 0 alebo viac pozičných
}
./program a.txt b.txt c.txt
# args.subory = ["a.txt", "b.txt", "c.txt"]
./program
# args.subory = []
Minimálne 1
#[derive(Parser)]
struct Args {
/// Súbory na spracovanie (aspoň 1)
#[arg(required = true)]
subory: Vec<String>,
}
./program a.txt b.txt # OK
./program # CHYBA: required
Pomenovaný s viacerými hodnotami
#[derive(Parser)]
struct Args {
#[arg(short, long)]
tagy: Vec<String>,
}
./program --tagy rust --tagy cli --tagy serde
# args.tagy = ["rust", "cli", "serde"]
./program -t rust -t cli -t serde
# to isté
Úlohy 8
8a. Napíš CLI, ktoré berie zoznam čísel a vypočíta ich súčet:
./sucet 1 2 3 4 5
# Súčet: 15
Hint: pozičný Vec<i32>.
8b. Napíš CLI s pomenovaným argumentom --vyluc ktorý berie viacero mien. Potom vypíš "Vylúčení: Anna, Boris".
Kapitola 9: Validácia argumentov
value_parser — kontrola rozsahu
#[derive(Parser)]
struct Args {
/// Port (1-65535)
#[arg(short, long, value_parser = clap::value_parser!(u16).range(1..=65535))]
port: u16,
/// Počet (1-100)
#[arg(short, long, value_parser = clap::value_parser!(u32).range(1..=100))]
pocet: u32,
}
./program --port 8080 --pocet 50 # OK
./program --port 0 # CHYBA: "0 is not in 1..=65535"
./program --pocet 200 # CHYBA
PathBuf argument
Na skúške cesta k súboru je vždy PathBuf:
use std::path::PathBuf;
#[derive(Parser)]
struct Args {
/// Cesta k dátovému súboru
#[arg(short, long, default_value = "data.json")]
subor: PathBuf,
}
fn main() {
let args = Args::parse();
// args.subor je PathBuf — môžeš ho priamo použiť
let kniznica = Kniznica::nacitaj_zo_suboru(&args.subor)
.unwrap_or_default();
}
./program --subor /cesta/k/suboru.json
./program -s data.json
./program # default: data.json
Úlohy 9
9a. Napíš Args s argumentom --vek typu u8, s rozsahom 0 až 150.
9b. Napíš Args s PathBuf argumentom --vstup (povinný) a --vystup (voliteľný, default "output.json").
Kapitola 10: Kompletný vzor zo skúšky
Vzor: Knižnica CLI
use clap::{Parser, Subcommand, ValueEnum};
use serde::{Serialize, Deserialize};
use std::fs;
use std::path::PathBuf;
// === Typy ===
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ValueEnum)]
pub enum Stav {
Nova,
Pouzivana,
Poskodena,
Vyradena,
}
impl Default for Stav {
fn default() -> Self {
Stav::Nova
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Kniha {
pub nazov: String,
pub autor: String,
pub rok: u16,
pub stav: Stav,
}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct Kniznica {
pub knihy: Vec<Kniha>,
}
// === serde I/O ===
impl Kniznica {
pub fn nacitaj_zo_suboru(cesta: &PathBuf) -> Option<Kniznica> {
let raw = fs::read_to_string(cesta).ok()?;
serde_json::from_str(&raw).ok()
}
pub fn uloz_do_suboru(&self, cesta: &PathBuf) -> bool {
let Ok(json) = serde_json::to_string_pretty(&self) else {
return false;
};
fs::write(cesta, json).is_ok()
}
pub fn pridaj(&mut self, kniha: Kniha) -> Result<(), String> {
if self.knihy.iter().any(|k| k.nazov == kniha.nazov) {
return Err(format!("Kniha '{}' už existuje", kniha.nazov));
}
self.knihy.push(kniha);
Ok(())
}
pub fn odstran(&mut self, nazov: &str) -> Result<Kniha, String> {
let pos = self.knihy.iter()
.position(|k| k.nazov == nazov)
.ok_or(format!("Kniha '{}' nenájdená", nazov))?;
Ok(self.knihy.remove(pos))
}
}
// === CLI ===
/// Správca knižnice
#[derive(Parser)]
#[command(about, version)]
struct Args {
/// Cesta k JSON súboru
#[arg(short, long, default_value = "kniznica.json")]
subor: PathBuf,
#[command(subcommand)]
prikaz: Prikaz,
}
#[derive(Subcommand)]
enum Prikaz {
/// Pridaj novú knihu
Pridaj {
/// Názov knihy
nazov: String,
/// Autor knihy
autor: String,
/// Rok vydania
#[arg(short, long)]
rok: u16,
/// Stav knihy
#[arg(short, long, default_value = "nova")]
stav: Stav,
},
/// Odstráň knihu
Odstran {
/// Názov knihy
nazov: String,
},
/// Vypíš všetky knihy
Vypis {
/// Filtrovať podľa stavu
#[arg(short, long)]
stav: Option<Stav>,
},
/// Hľadaj knihy podľa autora
Hladaj {
/// Meno autora
autor: String,
},
}
fn main() {
let args = Args::parse();
// Načítaj alebo vytvor prázdnu
let mut kniznica = Kniznica::nacitaj_zo_suboru(&args.subor)
.unwrap_or_default();
match args.prikaz {
Prikaz::Pridaj { nazov, autor, rok, stav } => {
let kniha = Kniha { nazov, autor, rok, stav };
match kniznica.pridaj(kniha) {
Ok(()) => println!("Kniha pridaná."),
Err(e) => println!("Chyba: {}", e),
}
}
Prikaz::Odstran { nazov } => {
match kniznica.odstran(&nazov) {
Ok(kniha) => println!("Odstránená: {} ({})", kniha.nazov, kniha.autor),
Err(e) => println!("Chyba: {}", e),
}
}
Prikaz::Vypis { stav } => {
let knihy: Vec<&Kniha> = match &stav {
Some(s) => kniznica.knihy.iter().filter(|k| &k.stav == s).collect(),
None => kniznica.knihy.iter().collect(),
};
if knihy.is_empty() {
println!("Žiadne knihy.");
} else {
for kniha in &knihy {
println!("{} — {} ({}) [{:?}]", kniha.nazov, kniha.autor, kniha.rok, kniha.stav);
}
}
}
Prikaz::Hladaj { autor } => {
let najdene: Vec<&Kniha> = kniznica.knihy.iter()
.filter(|k| k.autor.to_lowercase().contains(&autor.to_lowercase()))
.collect();
if najdene.is_empty() {
println!("Nič nenájdené.");
} else {
for kniha in &najdene {
println!("{} — {}", kniha.nazov, kniha.autor);
}
}
}
}
// Ulož
kniznica.uloz_do_suboru(&args.subor);
}
Spustenie:
./kniznica pridaj "Duna" "Frank Herbert" --rok 1965
./kniznica pridaj "Hobbit" "J.R.R. Tolkien" --rok 1937 --stav pouzivana
./kniznica vypis
./kniznica vypis --stav nova
./kniznica hladaj "Tolkien"
./kniznica odstran "Duna"
./kniznica --subor backup.json vypis
Úlohy 10
10a. Implementuj kompletný CLI pre Správcu úloh (Task Manager):
Štruktúra: Uloha { nazov: String, priorita: Priorita, hotova: bool }
Enum: Priorita { Nizka, Stredna, Vysoka }
Subcommands:
- pridaj <nazov> --priorita <priorita>
- hotovo <nazov>
- odstran <nazov>
- vypis [--priorita <priorita>]
- statistiky (počet celkom, hotových, nehotových)
Spoločný: --subor (PathBuf, default "ulohy.json")
10b. Implementuj kompletný CLI pre Milionár kvíz:
Subcommands:
- pridaj-otazku <text> --odpovede <4 odpovede> --spravna <index>
- hraj
- vypis-otazky
Spoločný: --subor (PathBuf, default "otazky.json")
10c. Implementuj kompletný CLI pre Adresár:
Štruktúra: Kontakt { meno: String, telefon: String, email: Option<String> }
Subcommands:
- pridaj <meno> <telefon> [--email <email>]
- odstran <meno>
- hladaj <vyraz>
- vypis [--zoradit]
- export --format <json|csv>
Spoločný: --subor (PathBuf, default "kontakty.json")
Prehľad: clap atribúty
| Atribút | Kde | Čo robí |
|---|---|---|
#[derive(Parser)] |
štruktúra | Hlavná CLI štruktúra |
#[derive(Subcommand)] |
enum | Podpríkazy |
#[derive(ValueEnum)] |
enum | Enum ako hodnota argumentu |
#[command(subcommand)] |
pole | Toto pole je subcommand |
#[command(about, version)] |
štruktúra | Pridaj popis a verziu |
#[arg(short, long)] |
pole | Pomenovaný argument |
#[arg(short = 'x')] |
pole | Vlastné short písmeno |
#[arg(long = "nazov")] |
pole | Vlastný long názov |
#[arg(default_value = "...")] |
pole | Default pre String/PathBuf |
#[arg(default_value_t = N)] |
pole | Default pre čísla/bool |
#[arg(required = true)] |
pole | Vynúť aj Vec |
#[arg(value_parser = ...)] |
pole | Validácia |
/// komentár |
štruktúra/pole | Help text |
Prehľad: typy polí
| Typ poľa | Správanie |
|---|---|
String |
Povinný argument |
Option<String> |
Voliteľný argument |
bool |
Flag (true/false) |
Vec<String> |
Viacero hodnôt |
PathBuf |
Cesta k súboru |
u32, i32, f64... |
Automaticky parsované |
MojEnum (ValueEnum) |
Povolené hodnoty |