Skip to main content

odds/persistence/
persistable.rs

1use anyhow::{Context, Ok};
2use serde::{Serialize, de::DeserializeOwned};
3use std::{fs, path::PathBuf};
4
5use crate::paths;
6
7pub trait Persistable: Serialize + DeserializeOwned + Sized {
8    const FILE: &'static str;
9
10    fn before_save(&mut self) {}
11
12    fn save(&mut self) -> anyhow::Result<()> {
13        // A hook for session so it can update its timestamp
14        self.before_save();
15
16        let path = Self::path();
17
18        if let Some(parent) = path.parent() {
19            fs::create_dir_all(parent)
20                .with_context(|| format!("Failed to create directory structure: {:?}", parent))?;
21        }
22
23        let contents = serde_json::to_string_pretty(self).with_context(|| "Failed to serialise data to JSON.")?;
24
25        fs::write(&path, contents).with_context(|| format!("Failed to write persistence file to {:?}", path))?;
26
27        Ok(())
28    }
29
30    fn load() -> anyhow::Result<Self> {
31        let path = Self::path();
32
33        let data = fs::read_to_string(&path).with_context(|| format!("Failed to read file: {:?}", path))?;
34        let result: Self =
35            serde_json::from_str(&data).with_context(|| format!("Failed to deserialise {}.", Self::FILE))?;
36
37        Ok(result)
38    }
39
40    fn reset() {
41        let path = Self::path();
42        if let Err(e) = fs::remove_file(&path) {
43            eprintln!("Failed to remove file: {:?}: {}", path, e);
44        }
45    }
46
47    fn path() -> PathBuf {
48        paths::persistence_path(Self::FILE)
49    }
50}