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}