85 lines
2.4 KiB
Rust
85 lines
2.4 KiB
Rust
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<T: DeserializeOwned + Default>(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<T: Serialize>(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<T> {
|
|
pub path: PathBuf,
|
|
pub cache: RwLock<(T, SystemTime)>,
|
|
}
|
|
|
|
impl<T: DeserializeOwned + Default + Serialize + Clone> Store<T> {
|
|
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<F: FnOnce(&mut T)>(&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();
|
|
}
|
|
}
|