Cél és előfeltétel
Egy jól olvasható riport nem változtatja meg véletlenül a sorrendjét két azonos feldolgozás között. Ebben a leckében BTreeMapből készítesz rendezett szöveget, majd a forrássorok megtartását és a duplikált kulcsok kezelését kapcsolod hozzá. Előfeltétel az iterátoros validáció, valamint a típusos hibák. Minden kód önálló, edition 2024 program beágyazott próbaadatokkal.
A stabil sorrendhez meg kell mondanod, mi szerint rendezel. A BTreeMap szöveges kulcsai Rust szerinti összehasonlítást használnak, nem magyar nyelvi szótári szabályt. Ezt a választást a riport szerződésének részeként kezeljük. A determinisztikus megjelenítés nem jelenti azt, hogy minden eltérő bemeneti fájl ugyanazt a riportot adja: az eredeti sorszámok például a dokumentum elrendezésével változhatnak.
Értéktábla és diagnosztikai eredet
Egy beállítás kulcsa és értéke a logikai tartalomhoz tartozik. A sorszám azt mondja meg, hol találtuk a forrásban. A kulcs szerinti rendezés nem írhatja át az eredeti sorszámot, mert az a hibakereséshez szükséges eredetadat. Ezért külön mezőben tároljuk, és nem az új megjelenítési pozícióból következtetünk rá.
A beépített insert művelet már létező kulcsnál lecserélheti az értéket. Egy konfigurációs nyelv választhatna „utolsó nyer” szabályt, de a mi végső parserünk ezt nem engedi: a duplikáció hiba. A rendezett adatstruktúra önmagában nem ellenőrzi ezt az üzleti szabályt. A felvételi logikában kell eldöntenünk, hogy új bejegyzésről vagy ütközésről van szó.
1. feladat: azonos tartalom, eltérő beszúrási sorrend
Írj kulcs=érték sorokat készítő függvényt. Két térkép ugyanazokat az adatokat fordított beszúrási sorrendben kapja. A riport bájtról bájtra legyen azonos, a sorokat mindig newline zárja. A példa kulcsai és értékei nem tartalmaznak sorvéget; ilyen adatot a soronkénti parser sem hozna létre egyetlen mezőként.
Megoldás
use std::collections::BTreeMap;
use std::fmt::Write;
fn render(values: &BTreeMap<&str, &str>) -> String {
let mut report = String::new();
for (key, value) in values {
writeln!(&mut report, "{key}={value}").unwrap();
}
report
}
fn main() {
let first = BTreeMap::from([("z", "2"), ("a", "x=y")]);
let second = BTreeMap::from([("a", "x=y"), ("z", "2")]);
assert_eq!(render(&first), "a=x=y\nz=2\n");
assert_eq!(render(&first), render(&second));
assert_eq!(first.get("a"), Some(&"x=y"));
assert_eq!(render(&BTreeMap::new()), "");
}
A jelentés nem változtatja meg a forrást. Az értékben lévő egyenlőségjel sem gond, mert a korábbi szerződés szerint a visszaolvasás csak az első jelnél bontana. A riport nem őrzi meg a kommenteket, szóközöket vagy a beszúrás eredeti sorrendjét. Ez normalizált megjelenítés, nem a bemeneti dokumentum bájtpontos rekonstrukciója.
2. feladat: rendezz a forrásszám átírása nélkül
A bejegyzéshez érték és eredeti sor is tartozzon. Készíts diagnosztikai riportot, amely kulcs szerint rendez, de az eredeti helyet írja ki. Ellenőrizd, hogy az elsőként megjelenő kulcs sorszáma nem feltétlenül egy. A diagnosztikai formátum most nem a konfiguráció visszaolvasási formátuma.
Megoldás
use std::collections::BTreeMap;
use std::fmt::Write;
struct Item<'a> { value: &'a str, line: usize }
fn main() {
let items = BTreeMap::from([
("z", Item { value: "2", line: 1 }),
("a", Item { value: "1", line: 4 }),
]);
let mut report = String::new();
for (key, item) in &items {
writeln!(&mut report, "{key}={} (eredeti sor: {})",
item.value, item.line).unwrap();
}
assert_eq!(report, "a=1 (eredeti sor: 4)\nz=2 (eredeti sor: 1)\n");
assert_eq!(items.get("a").unwrap().line, 4);
}
A kimeneti sorrend és az eredet két külön koordináta. Ha a felhasználó egy üres sort beszúr a dokumentum elejére, a logikai beállítások maradhatnak azonosak, a diagnosztikai sorhelyek viszont változnak. Ezt nem érdemes „nem determinisztikusnak” nevezni: az eredmény a megváltozott bemeneti eredetet tükrözi. A tesztben mindig mondd meg, mely tulajdonságok egyezését várod.
3. feladat: duplikáció ne írja felül az első értéket
Írj egyetlen bejegyzést felvevő függvényt Entry segítségével. Már létező kulcsnál Err legyen az eredmény, a korábbi érték pedig maradjon meg. Ugyanaz a kulcs ugyanazzal az értékkel is duplikáció ebben a nyelvben. A helyi bejegyzési szabályt majd a végső parser kapcsolja az eredeti sorszámhoz.
Megoldás
use std::collections::{BTreeMap, btree_map::Entry};
fn add<'a>(values: &mut BTreeMap<&'a str, &'a str>, key: &'a str, value: &'a str)
-> Result<(), &'static str>
{
match values.entry(key) {
Entry::Vacant(slot) => { slot.insert(value); Ok(()) }
Entry::Occupied(_) => Err("duplikalt kulcs"),
}
}
fn main() {
let mut values = BTreeMap::new();
add(&mut values, "port", "8080").unwrap();
assert_eq!(add(&mut values, "port", "9090"), Err("duplikalt kulcs"));
assert_eq!(add(&mut values, "port", "8080"), Err("duplikalt kulcs"));
assert_eq!(values.get("port"), Some(&"8080"));
assert_eq!(values.len(), 1);
}
A foglalt ág egyáltalán nem helyettesíti a korábbi értéket. Az elutasítás és a megőrzött tartalom együttes állítása erősebb, mint pusztán az Err ellenőrzése. A példában a kulcs pontos szövegegyezés szerint számít azonosnak. Kisbetűsítést vagy Unicode-normalizálást nem vezetünk be csendben, mert azok megváltoztatnák az azonos kulcs jelentését.
A riport formátuma is változtatható szerződés
Ha a kimenetet később más program olvassa, a sorvég, az elválasztó és az üres dokumentum alakja ugyanúgy számít, mint a szép megjelenítés. Ne adj hozzá magyarázó fejlécet egy már feldolgozott adatkimenethez külön döntés nélkül. Egy embernek szánt diagnosztikai szöveg és egy visszaolvasható konfiguráció eltérő formátum lehet, ezért a két függvényt is érdemes külön tartani.
A stabil riport segít a változások áttekintésében, de nem biztonsági hitelesítés és nem titokvédelmi mechanizmus. Jelszó vagy token értékét nem indokolt automatikusan diagnosztikába írni attól, hogy szépen rendezhető. A mai beágyazott példák nem tartalmaznak titkot; egy valódi felületben külön kellene meghatározni a látható és maszkolandó mezőket.
A rendezett megjelenítéshez az összes szükséges kulcsot tároljuk. Ez más költségmodell, mint a beérkező sorok azonnali továbbírása. A helyes sorrendért vállalt tárolási döntést ne nevezd önmagában hibának vagy optimalizálásnak: a felhasználói szerződésből következik. Nagy bemenet esetén a méretkorlátot és a feldolgozás módját ehhez kell igazítani.
Elfogadási feltételek
- Azonos kulcs-érték tartalom beszúrási sorrendtől függetlenül azonos, rendezett riportot ad.
- A kulcs szerinti rendezés nem számozza át az eredeti forrássorokat.
- Duplikációkor az első érték változatlan marad, ugyanaz az érték ismételten sem írható be.
Összefoglaló és továbblépés
A konfiguráció-parser miniprojektben a sorvalidáció és a duplikációellenőrzés egyetlen, típusos hibát adó felületté áll össze. A végső eredmény csak teljes siker esetén lesz alkalmazható konfiguráció.