feat: m1 vertikaler durchstich — mod-lader, registries, attributberechnung, screen-stack

Ersetzt src/ vollstaendig: print_box, include_str! und enum ItemType fallen,
wie Ticket 01-03 es vorgezeichnet haben.

- data/: Mod-Lader mit zwei Suchpfaden, selbstheilender load_order.toml,
  drei Phasen und zweistufiger Deserialisierung (03/04)
- game/: Attributkatalog, Creature, Game::attribute ueber mehrere Quellen,
  Command mit Outcome (01/07/11/12)
- ui/: Screen-Stack mit Response, Grid-Widget, Press-Filter (06/11/12)
- mods/core/: echte TOML-Dateien auf der Platte statt include_str!

10 Tests, darunter der Borrow-Beweis fuer Registries-neben-Game, den
Ticket 12 nicht fuehren konnte.
This commit is contained in:
2026-08-12 15:39:17 +02:00
parent 9c5efab331
commit b39491916f
28 changed files with 3414 additions and 279 deletions

51
tests/boundaries.rs Normal file
View File

@@ -0,0 +1,51 @@
//! Ticket 11: `game/` kennt ratatui nicht — und zwar mechanisch geprueft.
//!
//! Ein Cargo-Workspace mit `game` als eigener Crate waere echte
//! Compiler-Durchsetzung und ist die Decke, falls es je ernst wird. Bis dahin
//! faengt dieser Test denselben Fehler und kostet keine Umstrukturierung.
use std::fs;
use std::path::Path;
fn rust_files(dir: &Path, out: &mut Vec<std::path::PathBuf>) {
for entry in fs::read_dir(dir).expect("Verzeichnis lesbar").flatten() {
let path = entry.path();
if path.is_dir() {
rust_files(&path, out);
} else if path.extension().is_some_and(|e| e == "rs") {
out.push(path);
}
}
}
#[test]
fn game_bleibt_ratatui_frei() {
let root = Path::new(env!("CARGO_MANIFEST_DIR"));
for dir in ["src/game", "src/data"] {
let mut files = Vec::new();
rust_files(&root.join(dir), &mut files);
assert!(!files.is_empty(), "{dir} sollte Dateien enthalten");
for file in files {
let source = fs::read_to_string(&file).expect("Datei lesbar");
// Kommentare ausnehmen. Ticket 11 hat „liest die Dateien und prueft
// auf ratatui" gesagt — die woertliche Fassung schlaegt an, sobald
// ein Doc-Kommentar erklaert, dass die Grenze existiert.
let offender = source
.lines()
.enumerate()
.find(|(_, l)| !l.trim_start().starts_with("//") && l.contains("ratatui"));
if let Some((n, line)) = offender {
panic!(
"{}:{}: „{}\" — die Grenze aus Ticket 11 ist gefallen",
file.display(),
n + 1,
line.trim()
);
}
}
}
}