77 lines
2.8 KiB
Rust
77 lines
2.8 KiB
Rust
use crate::models::card::CardDefinition;
|
|
use anyhow::{Context, Result};
|
|
use std::collections::HashMap;
|
|
use std::path::Path;
|
|
|
|
/// In-memory card registry loaded from data/cards/*.json
|
|
pub struct CardDb {
|
|
pub cards: HashMap<String, CardDefinition>,
|
|
}
|
|
|
|
impl CardDb {
|
|
pub fn load(data_dir: &str) -> Result<Self> {
|
|
let cards_dir = Path::new(data_dir).join("cards");
|
|
let mut cards = HashMap::new();
|
|
|
|
if !cards_dir.exists() {
|
|
tracing::warn!("Card data directory not found: {:?}", cards_dir);
|
|
return Ok(Self { cards });
|
|
}
|
|
|
|
for entry in std::fs::read_dir(&cards_dir)
|
|
.with_context(|| format!("reading cards dir {:?}", cards_dir))?
|
|
{
|
|
let entry = entry?;
|
|
let path = entry.path();
|
|
if path.extension().map(|e| e == "json").unwrap_or(false) {
|
|
let content = std::fs::read_to_string(&path)
|
|
.with_context(|| format!("reading {:?}", path))?;
|
|
let batch: Vec<CardDefinition> = serde_json::from_str(&content)
|
|
.with_context(|| format!("parsing {:?}", path))?;
|
|
for card in batch {
|
|
cards.insert(card.id.clone(), card);
|
|
}
|
|
}
|
|
}
|
|
|
|
tracing::info!("Loaded {} card definitions", cards.len());
|
|
Ok(Self { cards })
|
|
}
|
|
|
|
/// Merge a game's **opt-in development content pack** from
|
|
/// `{data_dir}/games/{game}/dev/cards.json` (a single `CardDefinition[]`).
|
|
/// This is NOT read by [`CardDb::load`]; it is loaded only when a game is
|
|
/// explicitly named in `Config::dev_content_games`, so default content stays
|
|
/// untouched. Returns the number of definitions merged. A missing file is an
|
|
/// error (opt-in means the pack is expected to exist).
|
|
pub fn load_game_dev(&mut self, data_dir: &str, game: &str) -> Result<usize> {
|
|
let path = Path::new(data_dir)
|
|
.join("games")
|
|
.join(game)
|
|
.join("dev")
|
|
.join("cards.json");
|
|
let content = std::fs::read_to_string(&path)
|
|
.with_context(|| format!("reading dev 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 {} dev card definitions for game '{}'", n, game);
|
|
Ok(n)
|
|
}
|
|
|
|
pub fn get(&self, id: &str) -> Option<&CardDefinition> {
|
|
self.cards.get(id)
|
|
}
|
|
|
|
pub fn all(&self) -> Vec<&CardDefinition> {
|
|
self.cards.values().collect()
|
|
}
|
|
|
|
pub fn by_min_overall(&self, min: u8) -> Vec<&CardDefinition> {
|
|
self.cards.values().filter(|c| c.overall >= min).collect()
|
|
}
|
|
}
|