use std::path::Path; use std::sync::Arc; use logic::{table, LogicDataRef, LogicDataTables}; use crate::config::{CardRef, DataSelector}; pub const GOLD_RESOURCE_FALLBACK_INSTANCE: i32 = 1; pub const ARENA_FALLBACK_INSTANCE: i32 = 1; pub struct Catalog { tables: Arc, } impl Catalog { pub fn empty() -> Self { Self { tables: Arc::new(LogicDataTables::empty()), } } pub fn load(root: Option<&Path>) -> Self { let Some(root) = root else { tracing::info!("no csv root configured, starter profiles must use explicit table ids"); return Self::empty(); }; match LogicDataTables::load_from_dir(root) { Ok(tables) => { tracing::info!( root = %root.display(), loaded = tables.loaded_files().len(), skipped = tables.skipped_files().len(), "logic data tables loaded" ); let tables = Arc::new(tables); if !LogicDataTables::install(Arc::clone(&tables)) { tracing::warn!("data tables were already installed for this process"); } Self { tables } } Err(error) => { tracing::warn!(root = %root.display(), %error, "falling back to explicit table ids"); Self::empty() } } } pub fn tables(&self) -> &LogicDataTables { &self.tables } pub fn is_loaded(&self) -> bool { self.tables.is_loaded() } pub fn resolve_card(&self, card: &CardRef) -> Option { match card { CardRef::Explicit { table, instance } => Some(LogicDataRef::of(*table, *instance)), CardRef::Named(name) => { let resolved = LogicDataRef::by_name(table::SPELLS, name); if resolved.is_none() { tracing::warn!(card = %name, "unknown card name, dropping it from the profile"); return None; } Some(resolved) } } } pub fn resolve( &self, selector: &DataSelector, table_index: i32, fallback: i32, ) -> LogicDataRef { match selector { DataSelector::Instance(instance) => LogicDataRef::of(table_index, *instance), DataSelector::Named(name) => { let resolved = LogicDataRef::by_name(table_index, name); if resolved.is_none() { tracing::warn!( name = %name, table = table_index, fallback, "unknown data name, using the fallback instance" ); return LogicDataRef::of(table_index, fallback); } resolved } } } pub fn card_pool(&self) -> Vec { const CARD_TABLES: [i32; 3] = [ table::SPELLS_CHARACTERS, table::SPELLS_BUILDINGS, table::SPELLS_OTHER, ]; let mut pool = Vec::new(); for card_table in CARD_TABLES { let Some(rows) = self.tables.table(card_table) else { continue; }; for row in rows.iter() { if row.boolean("NotInUse") { continue; } pool.push(LogicDataRef::from(std::sync::Arc::clone(row))); } } pool } pub fn resolve_arena(&self, selector: &DataSelector) -> LogicDataRef { self.resolve(selector, table::ARENAS, ARENA_FALLBACK_INSTANCE) } pub fn resolve_resource(&self, selector: &DataSelector) -> LogicDataRef { self.resolve(selector, table::RESOURCES, GOLD_RESOURCE_FALLBACK_INSTANCE) } }