Skip to main content

this_me/
storage.rs

1use std::fmt;
2use std::fs;
3use std::path::{Path, PathBuf};
4
5use serde_json::Value as JsonValue;
6
7use crate::kernel::{snapshot_from_json, snapshot_to_json, Kernel, KernelError, Snapshot};
8
9#[derive(Debug)]
10pub enum StorageError {
11    Io(std::io::Error),
12    Json(serde_json::Error),
13    Codec(crate::kernel::JsonCodecError),
14    Kernel(KernelError),
15}
16
17impl fmt::Display for StorageError {
18    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19        match self {
20            Self::Io(error) => write!(f, "{error}"),
21            Self::Json(error) => write!(f, "{error}"),
22            Self::Codec(error) => write!(f, "{error}"),
23            Self::Kernel(error) => write!(f, "{error}"),
24        }
25    }
26}
27
28impl std::error::Error for StorageError {}
29
30impl From<std::io::Error> for StorageError {
31    fn from(error: std::io::Error) -> Self {
32        Self::Io(error)
33    }
34}
35
36impl From<serde_json::Error> for StorageError {
37    fn from(error: serde_json::Error) -> Self {
38        Self::Json(error)
39    }
40}
41
42impl From<crate::kernel::JsonCodecError> for StorageError {
43    fn from(error: crate::kernel::JsonCodecError) -> Self {
44        Self::Codec(error)
45    }
46}
47
48impl From<KernelError> for StorageError {
49    fn from(error: KernelError) -> Self {
50        Self::Kernel(error)
51    }
52}
53
54pub trait MemoryStore {
55    fn load_snapshot(&self) -> Result<Option<Snapshot>, StorageError>;
56    fn save_snapshot(&self, snapshot: &Snapshot) -> Result<(), StorageError>;
57
58    fn load_kernel(&self) -> Result<Kernel, StorageError> {
59        match self.load_snapshot()? {
60            Some(snapshot) => Ok(Kernel::hydrate(snapshot)?),
61            None => Ok(Kernel::new()),
62        }
63    }
64
65    fn save_kernel(&self, kernel: &Kernel) -> Result<(), StorageError> {
66        self.save_snapshot(&kernel.export_snapshot())
67    }
68}
69
70#[derive(Debug, Clone)]
71pub struct JsonFileStore {
72    path: PathBuf,
73}
74
75impl JsonFileStore {
76    pub fn new(path: impl Into<PathBuf>) -> Self {
77        Self { path: path.into() }
78    }
79
80    pub fn path(&self) -> &Path {
81        &self.path
82    }
83}
84
85impl MemoryStore for JsonFileStore {
86    fn load_snapshot(&self) -> Result<Option<Snapshot>, StorageError> {
87        if !self.path.exists() {
88            return Ok(None);
89        }
90
91        let raw = fs::read_to_string(&self.path)?;
92        if raw.trim().is_empty() {
93            return Ok(None);
94        }
95
96        let json = serde_json::from_str::<JsonValue>(&raw)?;
97        Ok(Some(snapshot_from_json(&json)?))
98    }
99
100    fn save_snapshot(&self, snapshot: &Snapshot) -> Result<(), StorageError> {
101        if let Some(parent) = self.path.parent() {
102            fs::create_dir_all(parent)?;
103        }
104
105        let json = snapshot_to_json(snapshot);
106        let raw = serde_json::to_string_pretty(&json)?;
107        let tmp_path = self.path.with_extension(format!(
108            "{}tmp",
109            self.path
110                .extension()
111                .and_then(|extension| extension.to_str())
112                .map(|extension| format!("{extension}."))
113                .unwrap_or_default()
114        ));
115
116        fs::write(&tmp_path, raw)?;
117        fs::rename(&tmp_path, &self.path)?;
118        Ok(())
119    }
120}
121
122impl<S> MemoryStore for Option<S>
123where
124    S: MemoryStore,
125{
126    fn load_snapshot(&self) -> Result<Option<Snapshot>, StorageError> {
127        match self {
128            Some(store) => store.load_snapshot(),
129            None => Ok(None),
130        }
131    }
132
133    fn save_snapshot(&self, snapshot: &Snapshot) -> Result<(), StorageError> {
134        match self {
135            Some(store) => store.save_snapshot(snapshot),
136            None => Ok(()),
137        }
138    }
139}