atagen

roundabout

public
Star0
Watch0
93b435e669a5f6986da74da157a887bf25f9007a
1
ssh://brewery@pls.lobotomise.me:33/atagen/roundabout.git
src/store.rsRust35 lines1017 B
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
}