From 3078efd9286063f48db4f6fd0bb386283f2bbb96 Mon Sep 17 00:00:00 2001 From: Priec Date: Wed, 4 Mar 2026 15:55:29 +0100 Subject: [PATCH] serde json --- priprava/rust_priprava9.md | 1031 ++++++++++++++++++++++++++++++++++++ riesenie1/Cargo.lock | 123 +++++ riesenie1/Cargo.toml | 9 + riesenie1/src/lib.rs | 118 +++++ riesenie1/src/main.rs | 3 + 5 files changed, 1284 insertions(+) create mode 100644 priprava/rust_priprava9.md create mode 100644 riesenie1/Cargo.lock create mode 100644 riesenie1/Cargo.toml create mode 100644 riesenie1/src/lib.rs create mode 100644 riesenie1/src/main.rs diff --git a/priprava/rust_priprava9.md b/priprava/rust_priprava9.md new file mode 100644 index 0000000..174b3ed --- /dev/null +++ b/priprava/rust_priprava9.md @@ -0,0 +1,1031 @@ +# serde a serde_json — krok za krokom + +Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej. + +`Cargo.toml`: +```toml +[dependencies] +serde = { version = "1", features = ["derive"] } +serde_json = "1" +``` + +```rust +use serde::{Serialize, Deserialize}; +``` + +--- + +## Kapitola 1: Čo je serde + +serde je framework na **serializáciu** a **deserializáciu** dát v Ruste. + +- **Serializácia** = Rust štruktúra → nejaký formát (JSON, TOML, YAML...) +- **Deserializácia** = nejaký formát → Rust štruktúra + +serde samotné nerobí nič s JSON. Je to len systém traitov. Konkrétny formát zabezpečuje knižnica ako `serde_json`. + +``` +Rust štruktúra ──Serialize──→ JSON string +JSON string ──Deserialize──→ Rust štruktúra +``` + +serde funguje cez dva traity: +- `Serialize` — štruktúra sa vie premeniť na dáta +- `Deserialize` — dáta sa vedia premeniť na štruktúru + +Neimplementuješ ich ručne. Použiješ `#[derive()]`: + +```rust +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize)] +struct Osoba { + meno: String, + vek: u32, +} +``` + +Toto je všetko. Teraz Osoba vie byť serializovaná a deserializovaná. + +### Úlohy 1 + +**1a.** Čo je serializácia? Čo je deserializácia? Povedz to vlastnými slovami. + +**1b.** Aký je rozdiel medzi `serde` a `serde_json`? + +**1c.** Napíš štruktúru `Kniha` s poľami `nazov: String`, `autor: String`, `rok: u16`. Pridaj derive pre Serialize a Deserialize. + +--- + +## Kapitola 2: Serialize — štruktúra → JSON + +```rust +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize)] +struct Osoba { + meno: String, + vek: u32, + email: String, +} + +fn main() { + let anna = Osoba { + meno: "Anna".to_string(), + vek: 25, + email: "anna@mail.sk".to_string(), + }; + + // to_string — kompaktný JSON (jeden riadok) + let json: String = serde_json::to_string(&anna).unwrap(); + println!("{}", json); + // {"meno":"Anna","vek":25,"email":"anna@mail.sk"} + + // to_string_pretty — pekne formátovaný JSON + let json_pretty: String = serde_json::to_string_pretty(&anna).unwrap(); + println!("{}", json_pretty); + // { + // "meno": "Anna", + // "vek": 25, + // "email": "anna@mail.sk" + // } +} +``` + +**Návratový typ:** `serde_json::to_string` vracia `Result`. Preto `.unwrap()` alebo `?` alebo `.ok()`. + +**Čo sa serializuje:** +- `String` → `"text"` +- čísla → čísla +- `bool` → `true`/`false` +- `Vec` → `[...]` +- `Option` → hodnota alebo `null` +- `HashMap` → `{...}` objekt +- vnorené štruktúry → vnorené objekty + +```rust +#[derive(Serialize, Deserialize)] +struct Kniznica { + nazov: String, + knihy: Vec, // → JSON pole + otvorena: bool, // → true/false + poznamka: Option, // → "text" alebo null +} +``` + +### Úlohy 2 + +**2a.** Vytvor štruktúru `Auto` s poľami `znacka: String`, `rok: u16`, `cena: f64`. Serializuj jedno auto do JSON. Vypíš kompaktne aj pretty. + +**2b.** Vytvor vektor 3 áut. Serializuj celý vektor do JSON. Aký formát má výstup? + +**2c.** Vytvor štruktúru `Garaz` s poľom `auta: Vec`. Serializuj garáž s 2 autami. Ako vyzerá JSON? + +**2d.** Čo sa stane ak pole je `Option` a je `None`? Serializuj a pozri. + +--- + +## Kapitola 3: Deserialize — JSON → štruktúra + +```rust +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize, Debug)] +struct Osoba { + meno: String, + vek: u32, +} + +fn main() { + let json = r#"{"meno":"Anna","vek":25}"#; + + // from_str — JSON string → štruktúra + let osoba: Osoba = serde_json::from_str(json).unwrap(); + println!("{:?}", osoba); + // Osoba { meno: "Anna", vek: 25 } + + // Typ musíš uviesť (Rust nevie aká štruktúra) + let osoba: Result = serde_json::from_str(json); + match osoba { + Ok(o) => println!("Meno: {}", o.meno), + Err(e) => println!("Chyba: {}", e), + } +} +``` + +**`r#"..."#`** — raw string literal. Netreba escapovať úvodzovky. Veľmi užitočné pre JSON. + +**Čo sa stane ak JSON nesedí?** + +```rust +// Chýba pole +let json = r#"{"meno":"Anna"}"#; +let vysledok: Result = serde_json::from_str(json); +// Err — chýba pole "vek" + +// Zlý typ +let json = r#"{"meno":"Anna","vek":"dvadsatpat"}"#; +let vysledok: Result = serde_json::from_str(json); +// Err — vek nie je číslo + +// Extra pole — OK! serde ho ignoruje +let json = r#"{"meno":"Anna","vek":25,"extra":"niečo"}"#; +let osoba: Osoba = serde_json::from_str(json).unwrap(); +// OK — extra pole sa ignoruje +``` + +### Úlohy 3 + +**3a.** Máš JSON: +```json +{"nazov":"Duna","autor":"Frank Herbert","rok":1965} +``` +Deserializuj ho do štruktúry `Kniha`. Vypíš názov a rok. + +**3b.** Máš JSON pole: +```json +[{"nazov":"A","rok":2000},{"nazov":"B","rok":2010}] +``` +Deserializuj ho do `Vec`. Vypíš počet kníh. + +**3c.** Čo sa stane ak JSON obsahuje pole, ktoré štruktúra nemá? Vyskúšaj. + +**3d.** Čo sa stane ak JSON-u chýba pole, ktoré štruktúra vyžaduje? Vyskúšaj. + +--- + +## Kapitola 4: Enumy v serde + +```rust +use serde::{Serialize, Deserialize}; + +// Jednoduchý enum (bez dát) — serializuje sa ako string +#[derive(Serialize, Deserialize, Debug)] +enum Farba { + Cervena, + Modra, + Zelena, +} + +let json = serde_json::to_string(&Farba::Cervena).unwrap(); +// "Cervena" + +let farba: Farba = serde_json::from_str(r#""Modra""#).unwrap(); +// Farba::Modra +``` + +```rust +// Enum s dátami — komplexnejší JSON +#[derive(Serialize, Deserialize, Debug)] +enum Sprava { + Text(String), + Cislo(i32), + Pozicia { x: f64, y: f64 }, + Prazdna, +} + +let json = serde_json::to_string(&Sprava::Text("ahoj".into())).unwrap(); +// {"Text":"ahoj"} + +let json = serde_json::to_string(&Sprava::Pozicia { x: 1.0, y: 2.0 }).unwrap(); +// {"Pozicia":{"x":1.0,"y":2.0}} + +let json = serde_json::to_string(&Sprava::Prazdna).unwrap(); +// "Prazdna" +``` + +Jednoduchý enum (bez dát) v štruktúre: + +```rust +#[derive(Serialize, Deserialize, Debug)] +enum Stav { + Nova, + Pouzivana, + Poskodena, + Vyradena, +} + +#[derive(Serialize, Deserialize, Debug)] +struct Kniha { + nazov: String, + stav: Stav, +} + +let kniha = Kniha { + nazov: "Duna".into(), + stav: Stav::Nova, +}; + +let json = serde_json::to_string_pretty(&kniha).unwrap(); +// { +// "nazov": "Duna", +// "stav": "Nova" +// } +``` + +### Úlohy 4 + +**4a.** Vytvor enum `Priorita` s hodnotami `Nizka, Stredna, Vysoka`. Serializuj každú hodnotu. Aký JSON produkujú? + +**4b.** Vytvor štruktúru `Uloha { nazov: String, priorita: Priorita }`. Serializuj jednu úlohu. Deserializuj JSON späť. + +**4c.** Vytvor enum s dátami: +```rust +enum Odpoved { + Spravna, + Nespravna { pokus: u32 }, + Nevyplnena, +} +``` +Serializuj každú variantu a pozri aký JSON generujú. + +--- + +## Kapitola 5: Súborový I/O — načítanie a uloženie + +Toto je na **každej** skúške. Vždy rovnaký vzor. + +### Uloženie do súboru + +```rust +use std::fs; +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize, Default)] +struct Kniznica { + knihy: Vec, +} + +impl Kniznica { + fn uloz_do_suboru(&self, cesta: &std::path::PathBuf) -> bool { + // 1. Serializuj na JSON string + let Ok(json) = serde_json::to_string_pretty(&self) else { + return false; + }; + // 2. Zapíš do súboru + fs::write(cesta, json).is_ok() + } +} +``` + +### Načítanie zo súboru + +```rust +impl Kniznica { + fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option { + // 1. Prečítaj súbor + let raw = fs::read_to_string(cesta).ok()?; + // 2. Deserializuj JSON string na štruktúru + serde_json::from_str(&raw).ok() + } +} +``` + +**Prečo `.ok()?`:** +- `fs::read_to_string` vracia `Result` +- `.ok()` ho premení na `Option` (zahodí chybu) +- `?` vráti `None` ak bol výsledok `None`, inak rozbalí hodnotu + +**Prečo posledný riadok nemá `?`:** +- `serde_json::from_str` vracia `Result` +- `.ok()` ho premení na `Option` +- Toto je posledný výraz funkcie, takže sa priamo vráti + +### Kompletný vzor (toto si zapamätaj naspamäť) + +```rust +use std::fs; +use std::path::PathBuf; +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize, Default)] +struct MojaStruktura { + data: Vec, +} + +impl MojaStruktura { + fn nacitaj_zo_suboru(cesta: &PathBuf) -> Option { + let raw = fs::read_to_string(cesta).ok()?; + serde_json::from_str(&raw).ok() + } + + 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() + } +} +``` + +Toto sa na skúške nikdy nemení. Len názov štruktúry sa zmení. + +### Úlohy 5 + +**5a.** Napíš `nacitaj_zo_suboru` a `uloz_do_suboru` pre: +```rust +#[derive(Serialize, Deserialize, Default)] +struct Adresar { + kontakty: Vec, +} +#[derive(Serialize, Deserialize, Clone)] +struct Kontakt { + meno: String, + telefon: String, +} +``` + +**5b.** Napíš program, ktorý: +1. Vytvorí `Adresar` s 2 kontaktmi +2. Uloží ho do `"adresar.json"` +3. Načíta ho späť +4. Vypíše počet kontaktov + +**5c.** Čo sa stane keď: +- Súbor neexistuje a voláš `nacitaj_zo_suboru`? +- Súbor obsahuje neplatný JSON? +- Súbor obsahuje platný JSON ale inej štruktúry? + +Vyskúšaj všetky 3 prípady. + +--- + +## Kapitola 6: Derive traity — čo kedy pridať + +Na skúške musíš vedieť aké derive traity pridať. Tu je prehľad: + +### Serialize a Deserialize + +```rust +#[derive(Serialize, Deserialize)] +``` + +**Kedy:** Vždy keď štruktúra/enum sa má ukladať do JSON alebo načítať z JSON. + +**Požiadavka:** Všetky polia musia tiež implementovať Serialize/Deserialize. Štandardné typy (String, i32, Vec, HashMap, Option...) to majú. + +### Default + +```rust +#[derive(Default)] +``` + +**Kedy:** Keď chceš vytvoriť inštanciu s defaultnými hodnotami. + +```rust +#[derive(Default, Serialize, Deserialize)] +struct Kniznica { + knihy: Vec, // default = prázdny Vec +} + +// Použitie: +let kniznica = Kniznica::default(); +// knihy = [] + +// Na skúške typicky: +let kniznica = Kniznica::nacitaj_zo_suboru(&cesta) + .unwrap_or_default(); // ak súbor neexistuje, prázdna knižnica +``` + +Defaultné hodnoty: +- `String` → `""` +- `Vec` → `vec![]` +- `i32, u32, f64...` → `0` +- `bool` → `false` +- `Option` → `None` + +### Clone + +```rust +#[derive(Clone)] +``` + +**Kedy:** Keď potrebuješ kópiu štruktúry. Toto potrebuješ ak: +- Vraciaš štruktúru z vektora (nie referenciu) +- Ukladáš kópiu niekam +- Voláš `.clone()` na štruktúre + +### Debug + +```rust +#[derive(Debug)] +``` + +**Kedy:** Keď chceš `println!("{:?}", struktura)`. Užitočné pri debugovaní. + +### PartialEq + +```rust +#[derive(PartialEq)] +``` + +**Kedy:** Keď chceš porovnávať `==` a `!=` medzi inštanciami. + +```rust +#[derive(PartialEq)] +enum Stav { Nova, Pouzivana } + +let a = Stav::Nova; +let b = Stav::Nova; +println!("{}", a == b); // true — funguje vďaka PartialEq +``` + +### Kombinované — typický vzor zo skúšky + +```rust +// Enum — zvyčajne všetko +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +enum Stav { + Nova, + Pouzivana, + Poskodena, + Vyradena, +} + +// Vnorená štruktúra — Serialize, Deserialize, Clone +#[derive(Debug, Clone, Serialize, Deserialize)] +struct Kniha { + nazov: String, + stav: Stav, +} + +// Hlavná štruktúra — Serialize, Deserialize, Default +#[derive(Debug, Serialize, Deserialize, Default)] +struct Kniznica { + knihy: Vec, +} +``` + +### Úlohy 6 + +**6a.** Zadanie hovorí: +``` +Enum Stav: Nova, Pouzivana, Poskodena, Vyradena +Požadované traity: Default, Display, Serialize, Deserialize, PartialEq +Default vráti hodnotu Nova +``` +Implementuj to. Pozor: `Default` a `Display` sa nedajú derive automaticky pre enum s vlastnou default hodnotou — musíš ich implementovať ručne. + +**6b.** Prečo knižnica (hlavná štruktúra) potrebuje `Default` ale kniha (vnorená štruktúra) nie? + +**6c.** Kedy potrebuješ `Clone` na štruktúre? Uveď 2 príklady kódu, kde by bez Clone kompilácia zlyhala. + +--- + +## Kapitola 7: Option v serde + +`Option` sa serializuje špeciálne: + +```rust +#[derive(Serialize, Deserialize, Debug)] +struct Profil { + meno: String, + prezyvka: Option, + vek: Option, +} + +// Some → hodnota +let profil = Profil { + meno: "Anna".into(), + prezyvka: Some("Anka".into()), + vek: Some(25), +}; +// {"meno":"Anna","prezyvka":"Anka","vek":25} + +// None → null +let profil = Profil { + meno: "Boris".into(), + prezyvka: None, + vek: None, +}; +// {"meno":"Boris","prezyvka":null,"vek":null} +``` + +**Deserializácia:** +```rust +// Pole chýba v JSON → None (ak je Option) +let json = r#"{"meno":"Cyril"}"#; +// BEZ špeciálnej konfigurácie: CHYBA! serde čaká všetky polia. + +// S atribútom skip_serializing_if + default: +#[derive(Serialize, Deserialize, Debug)] +struct Profil { + meno: String, + #[serde(default)] // ak chýba v JSON, použi Default (None pre Option) + prezyvka: Option, + #[serde(default)] + vek: Option, +} +// Teraz: {"meno":"Cyril"} → Profil { meno: "Cyril", prezyvka: None, vek: None } +``` + +Na skúške väčšinou nemusíš riešiť `#[serde(default)]` — JSON vždy obsahuje všetky polia. Ale je dobré vedieť, že to existuje. + +### Úlohy 7 + +**7a.** Vytvor štruktúru `Student { meno: String, email: Option, vek: u32 }`. Serializuj študenta s emailom aj bez. Porovnaj JSON. + +**7b.** Deserializuj tento JSON na štruktúru: +```json +{"meno":"Anna","email":null,"vek":25} +``` +Čo bude hodnota poľa `email`? + +**7c.** Máš Vec. Vyfiltruj len tých, ktorí majú email (email.is_some()). Serializuj výsledok. + +--- + +## Kapitola 8: HashMap a Vec v serde + +### Vec → JSON pole + +```rust +#[derive(Serialize, Deserialize)] +struct Trieda { + nazov: String, + studenti: Vec, +} + +let trieda = Trieda { + nazov: "4.A".into(), + studenti: vec!["Anna".into(), "Boris".into()], +}; +// { +// "nazov": "4.A", +// "studenti": ["Anna", "Boris"] +// } +``` + +### HashMap → JSON objekt + +```rust +use std::collections::HashMap; + +#[derive(Serialize, Deserialize)] +struct Slovnik { + preklady: HashMap, +} + +let mut preklady = HashMap::new(); +preklady.insert("dog".into(), "pes".into()); +preklady.insert("cat".into(), "mačka".into()); + +let slovnik = Slovnik { preklady }; +// { +// "preklady": { +// "dog": "pes", +// "cat": "mačka" +// } +// } +``` + +**Dôležité:** HashMap kľúče musia byť String (alebo čísla) v JSON. JSON nepodporuje iné typy kľúčov. + +### Vnorené štruktúry + +```rust +#[derive(Serialize, Deserialize)] +struct Adresa { + ulica: String, + mesto: String, +} + +#[derive(Serialize, Deserialize)] +struct Osoba { + meno: String, + adresa: Adresa, // vnorený objekt + telefony: Vec, // pole +} + +// { +// "meno": "Anna", +// "adresa": { +// "ulica": "Hlavná 1", +// "mesto": "Bratislava" +// }, +// "telefony": ["+421 900 000 000"] +// } +``` + +### Úlohy 8 + +**8a.** Vytvor štruktúru `SpravcaHry` s poľom `slovnik: HashMap>` (kategória → slová). Serializuj s 2 kategóriami, každá s 3 slovami. Pozri JSON. + +**8b.** Deserializuj tento JSON: +```json +{ + "meno": "Knižnica", + "knihy": [ + {"nazov": "Duna", "rok": 1965}, + {"nazov": "Hobbit", "rok": 1937} + ] +} +``` +Aké štruktúry potrebuješ? + +**8c.** Máš `HashMap>`. Serializuj ho priamo (nie v štruktúre). Čo je výsledok? + +--- + +## Kapitola 9: serde_json::Value — dynamický JSON + +Niekedy nevieš aký tvar bude mať JSON. Alebo ti stačí prísť len k jednej hodnote. + +```rust +use serde_json::Value; + +let json = r#"{"meno":"Anna","vek":25,"aktívna":true}"#; + +// Parsuj do Value — generického JSON typu +let v: Value = serde_json::from_str(json).unwrap(); + +// Prístup k hodnotám cez ["kľúč"] +println!("{}", v["meno"]); // "Anna" +println!("{}", v["vek"]); // 25 +println!("{}", v["aktívna"]); // true +println!("{}", v["neexistuje"]); // null + +// Konverzia na Rust typy +let meno: Option<&str> = v["meno"].as_str(); // Some("Anna") +let vek: Option = v["vek"].as_u64(); // Some(25) +let aktivna: Option = v["aktívna"].as_bool(); // Some(true) +``` + +**Typy v Value:** +```rust +enum Value { + Null, + Bool(bool), + Number(Number), + String(String), + Array(Vec), + Object(Map), +} +``` + +**Vytváranie JSON dynamicky:** +```rust +use serde_json::json; + +// json! makro — vytvor Value rýchlo +let v = json!({ + "meno": "Anna", + "vek": 25, + "zaujmy": ["šach", "čítanie"], + "adresa": { + "mesto": "Bratislava" + } +}); + +println!("{}", v["zaujmy"][0]); // "šach" +``` + +### Úlohy 9 + +**9a.** Máš neznámy JSON. Parsuj ho do `Value`. Skontroluj či obsahuje kľúč "meno". Ak áno, vypíš hodnotu. + +**9b.** Vytvor JSON pomocou `json!` makra s vnoreným objektom a poľom. Serializuj ho do pretty stringu. + +**9c.** Kedy použiješ `Value` a kedy vlastnú štruktúru? + +--- + +## Kapitola 10: Atribúty — #[serde(...)] + +serde má atribúty, ktorými ovládaš ako sa serializuje/deserializuje. + +### rename — premenuj pole + +```rust +#[derive(Serialize, Deserialize)] +struct Osoba { + #[serde(rename = "full_name")] + meno: String, + vek: u32, +} +// {"full_name":"Anna","vek":25} +``` + +### rename_all — premenuj všetky polia + +```rust +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Osoba { + cele_meno: String, + datum_narodenia: String, +} +// {"celeMeno":"Anna","datumNarodenia":"2000-01-01"} + +// Ďalšie možnosti: +// "snake_case" → cele_meno +// "camelCase" → celeMeno +// "PascalCase" → CeleMeno +// "SCREAMING_SNAKE_CASE" → CELE_MENO +// "kebab-case" → cele-meno +``` + +### skip — vynechaj pole + +```rust +#[derive(Serialize, Deserialize)] +struct Osoba { + meno: String, + #[serde(skip)] // toto pole sa neserializuje ani nedeserializuje + docasne_data: String, +} +``` + +### default — default hodnota ak chýba v JSON + +```rust +#[derive(Serialize, Deserialize)] +struct Config { + #[serde(default)] + port: u16, // ak chýba → 0 + #[serde(default = "default_host")] + host: String, // ak chýba → "localhost" +} + +fn default_host() -> String { + "localhost".to_string() +} +``` + +### deny_unknown_fields — zakáž extra polia + +```rust +#[derive(Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +struct Osoba { + meno: String, + vek: u32, +} +// {"meno":"Anna","vek":25,"extra":"niečo"} → CHYBA! +``` + +### Úlohy 10 + +**10a.** Máš API, ktoré vracia JSON s camelCase kľúčmi: +```json +{"firstName":"Anna","lastName":"Nováková","dateOfBirth":"2000-01-01"} +``` +Napíš Rust štruktúru, ktorá toto deserializuje. Použi `#[serde(rename_all = "camelCase")]`. + +**10b.** Máš štruktúru s poľom `heslo: String`, ktoré sa nesmie objaviť v JSON. Použi `#[serde(skip)]`. + +**10c.** Máš config štruktúru. Niektoré polia sú voliteľné a majú default hodnotu. Použi `#[serde(default)]`. + +--- + +## Kapitola 11: Chybové stavy a bezpečné spracovanie + +### Result z serde_json + +Obe hlavné funkcie vracajú Result: + +```rust +// Serializácia — skoro nikdy nezlyhá (len ak typ nie je serializovateľný) +let vysledok: Result = serde_json::to_string(&data); + +// Deserializácia — zlyhá často (neplatný JSON, zlá štruktúra) +let vysledok: Result = serde_json::from_str(json); +``` + +### Vzory spracovania chýb + +```rust +// 1. unwrap — program spadne ak chyba (len pre testovacie účely!) +let data: MojTyp = serde_json::from_str(json).unwrap(); + +// 2. ? — propaguj chybu nahor +fn spracuj(json: &str) -> Result { + let data: MojTyp = serde_json::from_str(json)?; + Ok(data) +} + +// 3. .ok() — zahoď chybu, vráť Option +let data: Option = serde_json::from_str(json).ok(); + +// 4. .ok()? — v funkcii vracajúcej Option (TOTO JE VZOR ZO SKÚŠKY) +fn nacitaj(cesta: &PathBuf) -> Option { + let raw = fs::read_to_string(cesta).ok()?; + serde_json::from_str(&raw).ok() +} + +// 5. match — keď chceš reagovať na chybu +match serde_json::from_str::(json) { + Ok(data) => println!("OK: {:?}", data), + Err(e) => println!("Chyba: {}", e), +} + +// 6. let..else — keď chceš hodnotu alebo early return +let Ok(json) = serde_json::to_string_pretty(&self) else { + return false; +}; +``` + +### Úlohy 11 + +**11a.** Napíš funkciu `bezpecne_parsuj(json: &str) -> Option` — vráti None pri akejkoľvek chybe. + +**11b.** Napíš funkciu `parsuj_alebo_default(json: &str) -> Kniznica` — ak sa nepodarí parsovať, vráti prázdnu knižnicu. Použi `.unwrap_or_default()`. + +**11c.** Napíš funkciu `nacitaj_a_pridaj(cesta: &PathBuf, kniha: Kniha) -> bool`: +1. Načítaj knižnicu zo súboru (ak neexistuje, vytvor prázdnu) +2. Pridaj knihu +3. Ulož späť +4. Vráť true ak sa podarilo + +--- + +## Kapitola 12: Praktické vzory zo skúšky + +### Vzor 1: Kompletný skúškový vzor (lib.rs) + +```rust +use serde::{Serialize, Deserialize}; +use std::collections::HashMap; +use std::fs; +use std::path::PathBuf; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +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, +} + +impl Kniznica { + pub fn nacitaj_zo_suboru(cesta: &PathBuf) -> Option { + 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_knihu(&mut self, kniha: Kniha) -> Result<(), ()> { + if self.knihy.iter().any(|k| k.nazov == kniha.nazov) { + return Err(()); + } + self.knihy.push(kniha); + Ok(()) + } + + // ... ďalšie metódy +} +``` + +### Vzor 2: main.rs s načítaním + +```rust +use std::path::PathBuf; + +fn main() { + let cesta = PathBuf::from("kniznica.json"); + + // Načítaj alebo vytvor novú + let mut kniznica = Kniznica::nacitaj_zo_suboru(&cesta) + .unwrap_or_default(); + + // Urob niečo + let kniha = Kniha { + nazov: "Duna".into(), + autor: "Herbert".into(), + rok: 1965, + stav: Stav::Nova, + }; + let _ = kniznica.pridaj_knihu(kniha); + + // Ulož + kniznica.uloz_do_suboru(&cesta); +} +``` + +### Vzor 3: SpravcaHry s HashMap (Obesenec) + +```rust +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct SpravcaHry { + pub slovnik: HashMap>, +} + +impl SpravcaHry { + pub fn nacitaj_zo_suboru(cesta: &PathBuf) -> Option { + 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_kategoriu(&mut self, kategoria: &str) -> Result<(), ()> { + if self.slovnik.contains_key(kategoria) { + return Err(()); + } + self.slovnik.insert(kategoria.to_string(), Vec::new()); + Ok(()) + } + + pub fn pridaj_slovo(&mut self, kategoria: &str, slovo: &str) -> Result<(), ()> { + let slova = self.slovnik.get_mut(kategoria).ok_or(())?; + slova.push(slovo.to_string()); + Ok(()) + } +} +``` + +JSON pre SpravcaHry: +```json +{ + "slovnik": { + "zvierata": ["pes", "mačka", "kôň"], + "jedlo": ["pizza", "burger", "šalát"] + } +} +``` + +### Úlohy 12 + +**12a.** Implementuj kompletný vzor pre **Milionár** zo skúšky: +``` +Štruktúra Odpoved: text (String), je_spravna (bool) +Štruktúra Otazka: text (String), odpovede (Vec) +Štruktúra Milionar: otazky (Vec) + ++ nacitaj_zo_suboru, uloz_do_suboru +``` +Pridaj správne derive traity. Vytvor testovacie dáta, ulož do JSON, načítaj späť. + +**12b.** Vytvor JSON ručne (v .json súbore alebo v kóde cez `r#"..."#`). Potom ho načítaj do tvojej štruktúry. Over, že všetky hodnoty sedia. + +**12c.** Napíš funkciu, ktorá načíta ľubovoľný JSON ako `Value`, overí či obsahuje kľúč `"verzia"`, a ak áno, vypíše jeho hodnotu. diff --git a/riesenie1/Cargo.lock b/riesenie1/Cargo.lock new file mode 100644 index 0000000..55695cc --- /dev/null +++ b/riesenie1/Cargo.lock @@ -0,0 +1,123 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "riesenie1" +version = "0.1.0" +dependencies = [ + "itertools", + "serde", + "serde_json", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/riesenie1/Cargo.toml b/riesenie1/Cargo.toml new file mode 100644 index 0000000..c0aab81 --- /dev/null +++ b/riesenie1/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "riesenie1" +version = "0.1.0" +edition = "2024" + +[dependencies] +itertools = "0.14.0" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.149" diff --git a/riesenie1/src/lib.rs b/riesenie1/src/lib.rs new file mode 100644 index 0000000..f7f94cf --- /dev/null +++ b/riesenie1/src/lib.rs @@ -0,0 +1,118 @@ +// src/lib.rs +use std::fmt; +use std::fs::File; +use std::fs; +use std::collections::HashMap; +use std::io::Write; + +#[derive(Debug, Default, serde::Serialize, serde::Deserialize)] +pub struct Kniha { + autori: Vec, + nazov: String, + vydavatelstvo: String, + zaner: String, + pocet_stran: usize, + isbn: String, + rok_vydania: String, + pozicana: bool, + stav_knihy: Stav, +} + +impl fmt::Display for Kniha { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({:#?}, {}, {}, {}, {}, {}, {}, {}, {:#?})", + self.autori, + self.nazov, + self.vydavatelstvo, + self.zaner, + self.pocet_stran, + self.isbn, + self.rok_vydania, + self.pozicana, + self.stav_knihy, + ) + } +} + +#[derive(Debug, Default, serde::Serialize, serde::Deserialize, PartialEq)] +pub enum Stav { + #[default] + Nova, + Pouzivana, + Poskodena, + Vyradena +} + +impl fmt::Display for Stav { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Stav::Nova => write!(f, "Nova"), + Stav::Pouzivana => write!(f, "Pouzivana"), + Stav::Poskodena => write!(f, "Poskodena"), + Stav::Vyradena => write!(f, "Vyradena"), + } + } +} + +#[derive(Default, serde::Serialize, serde::Deserialize)] +pub struct Kniznica { + knihy: Vec +} + +impl Kniznica { + pub fn nacitaj_zo_suboru(r: &std::path::PathBuf) -> Option { + let d: String = std::fs::read_to_string(r).ok()?; + let r: Option = serde_json::from_str(&d).ok()?; + r + } + + pub fn uloz_do_suboru(&self, z: &std::path::PathBuf) -> bool { + let Ok(f) = File::create(z) else{ + return false; + }; + serde_json::to_writer_pretty(f, &self).is_ok() + } + + pub fn pridaj_knihu(&mut self, k: Kniha) -> Result<(), ()> { + if self.knihy.iter().map(|x| x.isbn.clone()).any(|y| y == k.isbn) { + return Err(()); + } + self.knihy.push(k); + Ok(()) + } + + pub fn odstran_knihu(&mut self, r: &str) -> Result { + let Some(k) = self.knihy.iter().position(|x| x.isbn == r) else { + return Err(()); + }; + let t = self.knihy.remove(k); + Ok(t) + } + + pub fn daj_knihu_podla_isbn(&self, i: &str) -> Option<&Kniha> { + self.knihy.iter().find(|x| x.isbn == i) + } + + pub fn daj_knihy_autora(&self, a: &str) -> Vec<&Kniha> { + self.knihy.iter().filter(|y| y.autori.contains(&a.to_string())).collect() + } + + pub fn daj_knihy_podla_stavu(&self, s: Stav) -> Vec<&Kniha> { + self.knihy.iter().filter(|x| x.stav_knihy == s).collect() + } + + pub fn vypis_vydavatelstva_a_pocet_knih(&self) { + let mut h: HashMap = HashMap::new(); + for x in self.knihy.iter() { + h.entry(x.vydavatelstvo.clone()).and_modify(|x| *x += 1).or_insert(1); + } + // self.knihy.iter().counts(); + for y in h.iter() { + println!("{}: {} knih", y.0, y.1) + } + } + + pub fn vypis_knihy_daneho_zanru(&self, zk: &str) { + self.knihy.iter().filter(|&x| x.zaner == zk).map(|x| println!("{:#?}", x)); + } +} diff --git a/riesenie1/src/main.rs b/riesenie1/src/main.rs new file mode 100644 index 0000000..2a778e4 --- /dev/null +++ b/riesenie1/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + +}