Files
mtgc/src-tauri/src/state.rs

107 lines
3.5 KiB
Rust
Raw Normal View History

//! App state: the open [`Catalog`] and fuzzy [`NameIndex`], plus the db paths.
use std::path::{Path, PathBuf};
use mtgc_core::{Catalog, NameIndex};
use serde::{Deserialize, Serialize};
/// Small app-shell preferences persisted across restarts (distinct from the
/// user's library data) — currently just which library file was last open.
#[derive(Default, Serialize, Deserialize)]
struct PersistedConfig {
library_path: Option<PathBuf>,
}
fn config_path(data_dir: &Path) -> PathBuf {
data_dir.join("config.json")
}
fn load_config(data_dir: &Path) -> PersistedConfig {
std::fs::read_to_string(config_path(data_dir))
.ok()
.and_then(|s| serde_json::from_str(&s).ok())
.unwrap_or_default()
}
fn save_config(data_dir: &Path, cfg: &PersistedConfig) {
if let Ok(s) = serde_json::to_string_pretty(cfg) {
let _ = std::fs::write(config_path(data_dir), s);
}
}
/// Managed application state, guarded by a `Mutex` in Tauri's state store.
pub struct AppState {
data_dir: PathBuf,
pub library_path: PathBuf,
pub reference_path: PathBuf,
pub catalog: Option<Catalog>,
pub index: Option<NameIndex>,
/// Set while `update_card_data` is running; `cancel_update_card_data`
/// flips it to abort the in-progress ingest.
pub ingest_cancel: Option<mtgc_core::ingest::CancelToken>,
}
impl AppState {
/// Initialize with the last-opened library path (or the default under the
/// app data dir, on first run) and open the catalog if a reference db
/// already exists.
pub fn new(data_dir: PathBuf) -> Self {
let cfg = load_config(&data_dir);
let library_path = cfg
.library_path
.unwrap_or_else(|| data_dir.join("library.db"));
let mut s = Self {
reference_path: data_dir.join("reference.db"),
library_path,
data_dir,
catalog: None,
index: None,
ingest_cancel: None,
};
// Best-effort open; failures just leave the catalog unloaded.
let _ = s.reopen();
s
}
/// Point at a different library file and persist it so the next launch
/// reopens the same one.
pub fn set_library_path(&mut self, path: PathBuf) {
self.library_path = path.clone();
save_config(
&self.data_dir,
&PersistedConfig {
library_path: Some(path),
},
);
}
/// (Re)open the catalog + rebuild the name index. Only opens when a reference
/// db exists — attaching a non-existent file would create an empty (tableless)
/// db and make every query fail.
pub fn reopen(&mut self) -> Result<(), String> {
if self.reference_path.exists() {
let catalog = Catalog::open(&self.library_path, &self.reference_path)
.map_err(|e| e.to_string())?;
let index = catalog.build_name_index().map_err(|e| e.to_string())?;
self.index = Some(index);
self.catalog = Some(catalog);
} else {
self.catalog = None;
self.index = None;
}
Ok(())
}
pub fn catalog(&self) -> Result<&Catalog, String> {
self.catalog
.as_ref()
.ok_or_else(|| "Card data not loaded yet — click 'Download card data'.".to_string())
}
pub fn index(&self) -> Result<&NameIndex, String> {
self.index
.as_ref()
.ok_or_else(|| "Card data not loaded yet — click 'Download card data'.".to_string())
}
}