| 1 | use std::{fs, io, path::PathBuf}; |
| 2 | |
| 3 | use serde::{Serialize, de::DeserializeOwned}; |
| 4 | |
| 5 | use crate::{client::Flock, paths}; |
| 6 | |
| 7 | fn path(name: &str) -> PathBuf { |
| 8 | paths::state_dir().join(format!("{name}.json")) |
| 9 | } |
| 10 | |
| 11 | pub fn load<T: DeserializeOwned + Default>(name: &str) -> T { |
| 12 | fs::read(path(name)) |
| 13 | .ok() |
| 14 | .and_then(|b| serde_json::from_slice(&b).ok()) |
| 15 | .unwrap_or_default() |
| 16 | } |
| 17 | |
| 18 | /// readers skip the lock, the rename keeps them from seeing half a file |
| 19 | pub fn update<T, R>(name: &str, f: impl FnOnce(&mut T) -> R) -> io::Result<R> |
| 20 | where |
| 21 | T: Serialize + DeserializeOwned + Default, |
| 22 | { |
| 23 | let lock = fs::OpenOptions::new() |
| 24 | .create(true) |
| 25 | .truncate(false) |
| 26 | .write(true) |
| 27 | .open(paths::state_dir().join(format!("{name}.lock")))?; |
| 28 | let _guard = Flock::exclusive(lock)?; |
| 29 | let mut value = load(name); |
| 30 | let out = f(&mut value); |
| 31 | let tmp = path(&format!("{name}.tmp")); |
| 32 | fs::write(&tmp, serde_json::to_vec(&value)?)?; |
| 33 | fs::rename(tmp, path(name))?; |
| 34 | Ok(out) |
| 35 | } |