use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
use tokio::sync::Mutex;
use super::Directory;
pub struct Store {
path: PathBuf,
lock: Mutex<()>,
}
impl Store {
pub fn new(root: &Path) -> Result<Self> {
std::fs::create_dir_all(root).with_context(|| format!("create state dir {root:?}"))?;
Ok(Store { path: root.join("accounts.json"), lock: Mutex::new(()) })
}
async fn read(&self) -> Result<Directory> {
match tokio::fs::read(&self.path).await {
Ok(data) => Ok(serde_json::from_slice(&data).with_context(|| format!("parse {:?}", self.path))?),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(Directory::default()),
Err(e) => Err(e.into()),
}
}
pub async fn load(&self) -> Result<Directory> {
let _guard = self.lock.lock().await;
self.read().await
}
pub async fn update<T>(&self, f: impl FnOnce(&mut Directory) -> Result<T>) -> Result<T> {
let _guard = self.lock.lock().await;
let mut dir = self.read().await?;
let out = f(&mut dir)?;
let data = serde_json::to_vec_pretty(&dir)?;
let tmp = self.path.with_extension("json.tmp");
tokio::fs::write(&tmp, data).await?;
tokio::fs::rename(&tmp, &self.path).await?;
Ok(out)
}
}