use std::fs; use std::io::Write; use std::path::PathBuf; use anyhow::Result; use serde::{de::DeserializeOwned, Serialize}; use crate::store::schema::{KeyStatsData, DrillHistoryData, ProfileData}; pub struct JsonStore { base_dir: PathBuf, } impl JsonStore { pub fn new() -> Result { let base_dir = dirs::data_dir() .unwrap_or_else(|| PathBuf::from(".")) .join("keydr"); fs::create_dir_all(&base_dir)?; Ok(Self { base_dir }) } fn file_path(&self, name: &str) -> PathBuf { self.base_dir.join(name) } fn load(&self, name: &str) -> T { let path = self.file_path(name); if path.exists() { match fs::read_to_string(&path) { Ok(content) => serde_json::from_str(&content).unwrap_or_default(), Err(_) => T::default(), } } else { T::default() } } fn save(&self, name: &str, data: &T) -> Result<()> { let path = self.file_path(name); let tmp_path = path.with_extension("tmp"); let json = serde_json::to_string_pretty(data)?; let mut file = fs::File::create(&tmp_path)?; file.write_all(json.as_bytes())?; file.sync_all()?; fs::rename(&tmp_path, &path)?; Ok(()) } pub fn load_profile(&self) -> ProfileData { self.load("profile.json") } pub fn save_profile(&self, data: &ProfileData) -> Result<()> { self.save("profile.json", data) } pub fn load_key_stats(&self) -> KeyStatsData { self.load("key_stats.json") } pub fn save_key_stats(&self, data: &KeyStatsData) -> Result<()> { self.save("key_stats.json", data) } pub fn load_drill_history(&self) -> DrillHistoryData { self.load("lesson_history.json") } pub fn save_drill_history(&self, data: &DrillHistoryData) -> Result<()> { self.save("lesson_history.json", data) } }