feat(content): production content-pack loader + referenced-definition preflight
Production real-profile content is loaded via an explicit path, NOT the dev-only OPENFUT_DEV_CONTENT_GAMES gate: - Config.content_packs from env OPENFUT_CONTENT_PACKS (comma-sep file paths). - CardDb::load_pack(path): merge an explicit CardDefinition[] production pack. - app::build loads dev packs then production packs. Preflight (app::build, always on): every owned_cards.card_id MUST resolve to a loaded CardDefinition. A real profile with owned players but even ONE missing definition fails LOUDLY instead of silently serving an empty /collection; an empty owned_cards table (fresh DB / tests) passes. 2 preflight integration tests (missing def fails, loaded def passes). clippy -D warnings clean; full suite 151 tests green.
This commit is contained in:
@@ -62,6 +62,23 @@ impl CardDb {
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
/// Merge an explicit PRODUCTION content pack file (a single
|
||||
/// `CardDefinition[]`). Unlike [`CardDb::load_game_dev`] this takes a direct
|
||||
/// path (the real-profile import emits one) and is the production content
|
||||
/// path — not gated behind dev content. Returns the number merged.
|
||||
pub fn load_pack(&mut self, path: &Path) -> Result<usize> {
|
||||
let content = std::fs::read_to_string(path)
|
||||
.with_context(|| format!("reading content pack {path:?}"))?;
|
||||
let batch: Vec<CardDefinition> =
|
||||
serde_json::from_str(&content).with_context(|| format!("parsing {path:?}"))?;
|
||||
let n = batch.len();
|
||||
for card in batch {
|
||||
self.cards.insert(card.id.clone(), card);
|
||||
}
|
||||
tracing::info!("Loaded {} production card definitions from {:?}", n, path);
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
pub fn get(&self, id: &str) -> Option<&CardDefinition> {
|
||||
self.cards.get(id)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user