use serde::{Serialize, de::DeserializeOwned}; use std::fs; use std::path::{Path, PathBuf}; use std::sync::RwLock; use std::time::SystemTime; pub fn read_json_file(path: &Path) -> T { if let Ok(data) = fs::read(path) && let Ok(parsed) = serde_json::from_slice(&data) { return parsed; } let bak_path = path.with_extension("json.bak"); if let Ok(data) = fs::read(&bak_path) && let Ok(parsed) = serde_json::from_slice(&data) { let _ = fs::write(path, data); return parsed; } T::default() } pub fn write_json_atomic(path: &Path, data: &T) -> std::io::Result<()> { if path.exists() { let bak_path = path.with_extension("json.bak"); let _ = fs::copy(path, &bak_path); } let tmp_path = path.with_extension("json.tmp"); let json_data = serde_json::to_string_pretty(data)?; fs::write(&tmp_path, json_data)?; fs::rename(&tmp_path, path) } pub struct Store { pub path: PathBuf, pub cache: RwLock<(T, SystemTime)>, } impl Store { fn mtime(&self) -> SystemTime { fs::metadata(&self.path) .and_then(|m| m.modified()) .unwrap_or(SystemTime::UNIX_EPOCH) } pub fn new(path: PathBuf) -> Self { let mtime = fs::metadata(&path) .and_then(|m| m.modified()) .unwrap_or(SystemTime::UNIX_EPOCH); let initial_data = read_json_file(&path); Self { path, cache: RwLock::new((initial_data, mtime)), } } pub fn read(&self) -> T { let current_mtime = self.mtime(); { let lock = self.cache.read().unwrap(); if lock.1 == current_mtime { return lock.0.clone(); } } let mut lock = self.cache.write().unwrap(); let new_mtime = self.mtime(); if lock.1 != new_mtime { lock.0 = read_json_file(&self.path); lock.1 = new_mtime; } lock.0.clone() } pub fn modify(&self, f: F) { let mut lock = self.cache.write().unwrap(); let current_mtime = self.mtime(); if lock.1 != current_mtime { lock.0 = read_json_file(&self.path); } f(&mut lock.0); let _ = write_json_atomic(&self.path, &lock.0); lock.1 = self.mtime(); } }