From 145b82bc0894d03249db9344d328511d419bcd62 Mon Sep 17 00:00:00 2001 From: MDietermann Date: Wed, 12 Aug 2026 09:45:55 +0200 Subject: [PATCH] docs: ticket 04 (fehlerstrategie) aufloesen, vorgaben an ticket 11 --- .../rustkeep-1.0/issues/04-fehlerstrategie.md | 109 +++++++++++++++++- .../issues/11-ui-geruest-screen-modell.md | 8 ++ .scratch/rustkeep-1.0/map.md | 1 + 3 files changed, 117 insertions(+), 1 deletion(-) diff --git a/.scratch/rustkeep-1.0/issues/04-fehlerstrategie.md b/.scratch/rustkeep-1.0/issues/04-fehlerstrategie.md index c99f871..f9243b8 100644 --- a/.scratch/rustkeep-1.0/issues/04-fehlerstrategie.md +++ b/.scratch/rustkeep-1.0/issues/04-fehlerstrategie.md @@ -1,7 +1,7 @@ # 04 — Fehlerstrategie für kaputte Mods Type: grilling -Status: open +Status: resolved Blocked by: 03 ## Question @@ -32,3 +32,110 @@ Offen bleibt damit: kaputte TOML-Syntax (die Datei ist unlesbar, die Einheit „ fehlende Pflichtfelder, unaufgelöste Referenzen aus Phase 3 — und wohin die Warnungen gehen, wenn der Bildschirm ratatui gehört. Ein Ladelauf produziert leicht ein Dutzend, und `println!` ist nach dem TUI-Start keine Option mehr. + +## Answer + +### Zwei Klassen, zwei Einheiten + +**Syntax ist ein Datei-Fehler, alles Semantische ein Eintrags-Fehler.** + +Das ist nicht gratis: `toml::from_str::()` mit `items: Vec` scheitert *komplett*, wenn +ein einziger Eintrag ein kaputtes Feld hat — das Versprechen aus Ticket 03 wäre damit genau dann +gebrochen, wenn 200 Gegenstände in einer Datei stehen, also im Normalfall. Deshalb wird zweistufig +deserialisiert: erst `Vec>` je Registry-Tabelle, dann jeder Eintrag einzeln in +seinen Typ. Etwa zehn Zeilen, und ein falsches Feld trifft nur noch seinen Eintrag. + +Bei kaputter TOML-Syntax fällt die ganze Datei — da kommt der Parser nicht weiter, das ist keine +Entscheidung, sondern eine Tatsache. + +### Harte Fehler — genau drei + +`depends` nicht installiert · `depends`-Reihenfolge verletzt · `core` nicht gefunden. Alle drei: +Meldung auf stderr, `exit(1)`. Das TUI startet gar nicht erst, damit ist stderr genau der richtige Ort. + +`core` ist darüber hinaus **kein Sonderfall**: Ein kaputter Eintrag darin ist eine Warnung wie überall. +Ein Sonderfall würde die Privilegierung wieder einbauen, die Ticket 03 gerade abgeschafft hat. Die +Absicherung gehört nicht in die Laufzeit, sondern in einen Test — siehe unten. Dass `core` fehlend +hart ist, widerspricht dem nicht: Ohne `core` gibt es kein Attribut, keinen Slot und keinen +Gegenstand, das ist nicht Privileg, sondern die Abwesenheit sämtlichen Inhalts. + +Ein nicht schreibbares Log ist ausdrücklich **kein** harter Fehler. Der Schreibfehler wird verschluckt; +ein Spiel, das wegen seiner Logdatei nicht startet, wäre die Karikatur guter Fehlerbehandlung. + +### Warnungen + +Während des Ladens in einem `Vec` gesammelt, am Ende nach +`data_dir()/rustkeep/logs/load-20260812-112305.log` geschrieben. Die letzten zehn bleiben liegen, +ältere werden beim Schreiben gelöscht — streng auf das Muster `load-*.log` im eigenen `logs/`-Ordner +beschränkt. Ein sauberer Lauf legt keine Datei an. + +Auf dem Startbildschirm steht **eine Zeile** mit Anzahl und Dateiname. Kein Report-Screen: Das wäre +ein Screen mit Scrolling und Navigation, den Ticket 11 mitschleppen müsste, für Inhalt, den ein +Texteditor besser anzeigt. + +Keine Schweregrade. Jeder Fall aus 03 und aus der Deserialisierung ist „Warnung, verworfen" — eine +zweite Stufe wäre ein Feld, das überall denselben Wert trägt, plus die Frage, was `INFO` auslöst. + +### Meldungsformat + +``` +meinmod/items.toml:42: core:iron_sword — unknown field `base_pric` +``` + +Die **Mod-ID steht vorne**: Bei zwölf installierten Mods ist sie die einzige Angabe, mit der ein +Spieler weiß, wen er anschreiben muss. + +`toml::de::Error` liefert `span() -> Option>` und `message()`, aber keine Zeile — die ist +`input[..span.start].lines().count()`, eine Zeile Code. Keine Spalte: `message()` nennt das Feld +bereits im Klartext, die Spalte wäre doppelt gemoppelt und bräuchte eine zweite Rechnung. Ein Eintrag +ohne lesbare `id` kann sich nicht benennen und heißt nach seiner Position: `items[3]`. + +### Rust-Seite + +Handgeschriebenes `enum LoadError` mit drei Varianten und eigenem `impl Display`. + +`thiserror` wäre ein Proc-Macro-Dependency (zieht `syn` und `quote` in den Build) für ungefähr zehn +gesparte Zeilen `match` — und in einem Ticket, das ausdrücklich Rust lernen soll, ist das Schreiben +dieses `match` der Zweck. `anyhow` ist für Anwendungen gedacht, denen der konkrete Fehlertyp egal ist; +hier ist er es nicht, es sind genau drei benannte Fälle. `impl std::error::Error` bleibt weg, solange +niemand `Box` braucht. + +```rust +fn load_mods() -> Result<(Registries, Vec), LoadError> +``` + +Warnungen reisen als zweiter Rückgabewert. Als Feld in `Registries` würden sie Diagnose an die +Spieldaten hängen, die danach das ganze Spiel lang mitfahren und in jedem Save-Gedanken auftauchen; +als `&mut Vec`-Parameter wäre es das C-Muster. Ein Tupel aus zwei Dingen braucht keinen +`LoadReport`-Typ — den gäbe es für genau eine Aufrufstelle. + +Die `Result`-Kette endet **vor** `main`: `eprintln!("{e}")` plus `exit(1)`. `main() -> Result<…>` +druckt über `Debug` und setzt dem Spieler `LoadError { .. }` samt „Error: " vor. + +### Panic-Grenze + +Die Grenze ist die **Datenherkunft**, nicht die Schwere: + +- Alles, was aus einer TOML kommt, ist `Result`. +- Alles, was aus dem eigenen Code kommt, darf panicken — Index in eine selbst gebaute `Vec`, + `unreachable!` im `match` über ein eigenes Enum. Ein Panic dort ist ein Engine-Bug und soll laut + sein; ihn in ein `Result` zu verpacken hieße, den Aufrufer nach etwas zu fragen, das er nicht + beantworten kann. + +`unwrap()` auf Modder-Daten ist ausnahmslos falsch — genau das tut `items.rs::parse()` heute. + +### Check + +Ein `#[test]`, der `mods/core/` lädt und auf **null Warnungen** prüft. Damit fällt ein kaputtes +Kernspiel beim `cargo test` auf statt beim Spieler, und die Ladelogik bleibt frei von Sonderregeln. +Das ist die eine runnable Prüfung, die dieses Ticket hinterlässt. + +### Auflagen an andere Tickets + +- **11 UI-Gerüst und Screen-Modell**: Der Startbildschirm trägt eine Zeile mit Warnungsanzahl und + Logdateiname. Eine Zeile, kein Screen. + +### Folgen für bestehenden Code + +`items.rs::parse()` verliert sein `panic!`, `main()` bekommt den stderr-plus-exit-Pfad, die +`logs/`-Verwaltung kommt neu dazu. diff --git a/.scratch/rustkeep-1.0/issues/11-ui-geruest-screen-modell.md b/.scratch/rustkeep-1.0/issues/11-ui-geruest-screen-modell.md index 384a538..86e817c 100644 --- a/.scratch/rustkeep-1.0/issues/11-ui-geruest-screen-modell.md +++ b/.scratch/rustkeep-1.0/issues/11-ui-geruest-screen-modell.md @@ -24,3 +24,11 @@ Entscheidungen darüber stehen noch aus: - **Welche Screens gibt es zum Start**, und welche Aktionen kennt `Game::apply(Action)`? Ergebnis: Screen-Enum, Stack-Modell, Action-Liste und die Regel, was `game/` nicht importieren darf. + +## Vorgaben aus anderen Tickets + +- **02**: Charakterbogen und Charakterbau sind Registry-Schleifen, keine gemauerten Layouts — + Begründung in [Ticket 02](02-typ-registry.md). +- **04**: Der Startbildschirm trägt eine Zeile mit Warnungsanzahl und Logdateiname + („12 Ladewarnungen — `logs/load-20260812-112305.log`"). **Eine Zeile, kein Screen** — der Inhalt + gehört in einen Texteditor, nicht in ein TUI mit Scrolling und Navigation. diff --git a/.scratch/rustkeep-1.0/map.md b/.scratch/rustkeep-1.0/map.md index 1d71565..dd5de49 100644 --- a/.scratch/rustkeep-1.0/map.md +++ b/.scratch/rustkeep-1.0/map.md @@ -62,6 +62,7 @@ auch über Ticket 13. - [01 — Effekt-Vokabular](issues/01-effekt-vokabular.md) ([#2](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/2)) — **Zwei Begriffe statt einem**: Modifikator (rechnet ein Attribut um, solange er existiert) und Aktion (ändert Zustand einmalig), getrennte Maschinen und getrennte TOML-Arrays. Fester Attributkatalog ohne Namensraum; `max_health` ist Attribut, der Lebensstand ist Zustand. Sechs Aktionen, `damage` eigenständig wegen der Minderungskette. Stapeln `(Basis + Σ flach) × (1 + Σ prozentual)`. Eine Uhr: der Zug. Sieben feste Schadenstypen, ein Minderungsmechanismus (`resistance`), `armor` nur Kurzschreibweise. Auren wohnen beim Träger. Bedingungsvokabular bewusst an Ticket 08 delegiert. Begriffe im [Glossar](../../CONTEXT.md). - [02 — Typ-Registry statt Enums](issues/02-typ-registry.md) ([#3](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/3)) — **Es gibt keinen Item-Typ mehr**: `enum ItemType` vermischte Slot, Verhalten und Kategorie und entfällt ersatzlos zugunsten der Felder `slot`, `stackable`, `consumed_on_use`. `ItemWeapon` und `ItemArmor` fallen weg, ein Gegenstand ist einheitlich. **Fähigkeit** kommt als dritter Begriff neben Modifikator und Aktion dazu (Angebot statt Wirkung). Fünf moddbare Registries; Slots und sogar Charakteroption-Kategorien sind Inhalt, mit `pick = N`. Startausrüstung ist `on_create` als gewöhnliche Aktionsliste. `value` → `base_price`, `shop_price` → `price_modifier`. - [03 — Mod-Ladepipeline und Manifest](issues/03-mod-ladepipeline.md) ([#4](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/4)) — Zwei Suchpfade: `mods/` neben der Binary (enthält `core/`) und `data_dir()/rustkeep/mods` über die Crate `directories`; bei gleicher ID gewinnt das Profil, mit Warnzeile. **`core` liegt als echter Mod-Ordner auf der Platte, `include_str!` entfällt.** `manifest.toml` mit vier Feldern (`id`, `name`, `version`, `depends`) — TOML statt YAML, weil `serde_yaml` und der Fork `serde_yml` beide archiviert sind. Ladereihenfolge aus einer selbstheilenden Spielerliste `load_order.toml`; `depends` sortiert nicht, sondern prüft, und eine Verletzung ist harter Startfehler. Jede `*.toml` im Mod wird eingelesen, jede darf jede Registry-Tabelle enthalten. Präfix wird automatisch vorangestellt, Überschreiben über `overrides = "core:iron_sword"` am Eintrag — der landet **nur** unter der überschriebenen ID. Drei Phasen (Manifeste → Einlesen → Auflösen), dadurch keine Vorwärtsreferenzen; Fehlereinheit ist der Eintrag. Kein Nachladen zur Laufzeit. +- [04 — Fehlerstrategie für kaputte Mods](issues/04-fehlerstrategie.md) ([#5](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/5)) — **Syntax ist ein Datei-Fehler, alles Semantische ein Eintrags-Fehler**; damit 03s Versprechen hält, wird zweistufig deserialisiert (`Vec>`, dann Eintrag für Eintrag), sonst reißt ein Tippfehler die ganze `items.toml` mit. Genau drei harte Fehler: `depends` fehlend, `depends` verletzt, `core` nicht gefunden — stderr und `exit(1)`. `core` ist sonst kein Sonderfall; abgesichert wird es durch einen `#[test]`, der `mods/core/` mit null Warnungen lädt. Warnungen sammeln sich in `Vec` und landen in `logs/load-.log` (letzte zehn), plus eine Zeile auf dem Startbildschirm — kein Report-Screen. Meldung `meinmod/items.toml:42: core:iron_sword — `, Mod-ID vorne, keine Spalte. Rust: handgeschriebenes `enum LoadError` mit `impl Display` statt `thiserror`/`anyhow`, `load_mods() -> Result<(Registries, Vec), LoadError>`, Kette endet vor `main` mit `eprintln!` und `exit(1)`. **Panic-Grenze ist die Datenherkunft**: TOML ist `Result`, eigener Code darf panicken. - [05 — Research: Script-Engine](issues/05-research-script-engine.md) ([#6](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/6)) — Empfehlung Rhai wegen Fehlermeldungen mit Zeile und Spalte, billiger Typ-Brücke, AST-Hot-Reload und Build ohne `cc`. Gemessen: mlua liest unter `StdLib::ALL_SAFE` per `io.open` echte Dateien — „safe" heißt dort speichersicher, nicht fähigkeitsbeschränkt. Lua gewinnt bei der Modder-Vertrautheit; WASM scheidet für 1.0 aus. **Kernfolge: reden Scripts nur über das Effekt-Vokabular, ist die Engine-Wahl umkehrbar.** Entscheidung offen in Ticket 13. - [06 — Research: ratatui-App-Architektur](issues/06-research-ratatui-architektur.md) ([#7](https://gitlab.hive-brain.de/MDietermann/rustkeep/issues/7)) — Screen-Stack über zentralem `Game`-State statt Elm oder Component-Trait; `game/` bleibt ratatui-frei; Popups per `Clear` auf Sub-`Rect` statt Widget-Stack; blockierendes `event::read()` ohne Tick; Dungeon-Grid ist Eigenbau, es gibt kein Widget dafür; ratatui 0.30.2 mit MSRV 1.88; `KeyEventKind::Press`-Filter unter Windows Pflicht. Voller Bericht in [research/06](research/06-ratatui-architektur.md).