eidetica/backend/database/in_memory/
persistence.rs1use std::{collections::HashMap, path::Path, sync::RwLock};
7
8use serde::{Deserialize, Deserializer, Serialize, Serializer};
9
10use super::{InMemory, InMemoryInner, TreeTipsCache};
11use crate::{
12 Error, Result,
13 backend::{InstanceMetadata, VerificationStatus, errors::BackendError},
14 entry::{Entry, ID},
15};
16
17const PERSISTENCE_VERSION: u8 = 0;
20
21fn is_v0(v: &u8) -> bool {
23 *v == 0
24}
25
26fn validate_persistence_version<'de, D>(deserializer: D) -> std::result::Result<u8, D::Error>
28where
29 D: Deserializer<'de>,
30{
31 use serde::Deserialize;
32 let version = u8::deserialize(deserializer)?;
33 if version != PERSISTENCE_VERSION {
34 return Err(serde::de::Error::custom(format!(
35 "unsupported persistence version {version}; only version {PERSISTENCE_VERSION} is supported"
36 )));
37 }
38 Ok(version)
39}
40
41#[derive(Serialize, Deserialize)]
43struct SerializableDatabase {
44 #[serde(
46 rename = "_v",
47 default,
48 skip_serializing_if = "is_v0",
49 deserialize_with = "validate_persistence_version"
50 )]
51 version: u8,
52 entries: HashMap<ID, Entry>,
53 #[serde(default)]
54 verification_status: HashMap<ID, VerificationStatus>,
55 #[serde(default)]
57 instance_metadata: Option<InstanceMetadata>,
58 #[serde(default)]
60 cache: HashMap<String, String>,
61 #[serde(default)]
63 tips: HashMap<ID, TreeTipsCache>,
64}
65
66impl Serialize for InMemory {
67 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
68 where
69 S: Serializer,
70 {
71 let serializable = {
73 let inner = self.inner.read().unwrap();
74 let crdt_cache = self.crdt_cache.read().unwrap();
75 SerializableDatabase {
76 version: PERSISTENCE_VERSION,
77 entries: inner.entries.clone(),
78 verification_status: inner.verification_status.clone(),
79 instance_metadata: inner.instance_metadata.clone(),
80 cache: crdt_cache.clone(),
81 tips: inner.tips.clone(),
82 }
83 };
84
85 serializable.serialize(serializer)
86 }
87}
88
89impl<'de> Deserialize<'de> for InMemory {
90 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
91 where
92 D: Deserializer<'de>,
93 {
94 let serializable = SerializableDatabase::deserialize(deserializer)?;
96
97 Ok(InMemory {
98 inner: RwLock::new(InMemoryInner {
99 entries: serializable.entries,
100 verification_status: serializable.verification_status,
101 instance_metadata: serializable.instance_metadata,
102 tips: serializable.tips,
103 }),
104 crdt_cache: RwLock::new(serializable.cache),
105 })
106 }
107}
108
109pub(crate) fn save_to_file<P: AsRef<Path>>(backend: &InMemory, path: P) -> Result<()> {
118 let serializable = {
120 let inner = backend.inner.read().unwrap();
121 let crdt_cache = backend.crdt_cache.read().unwrap();
122 SerializableDatabase {
123 version: PERSISTENCE_VERSION,
124 entries: inner.entries.clone(),
125 verification_status: inner.verification_status.clone(),
126 instance_metadata: inner.instance_metadata.clone(),
127 cache: crdt_cache.clone(),
128 tips: inner.tips.clone(),
129 }
130 };
131
132 let json = serde_json::to_string_pretty(&serializable)
133 .map_err(|e| -> Error { BackendError::SerializationFailed { source: e }.into() })?;
134 std::fs::write(path, json).map_err(|e| -> Error { BackendError::FileIo { source: e }.into() })
135}
136
137pub(crate) fn load_from_file<P: AsRef<Path>>(path: P) -> Result<InMemory> {
147 match std::fs::read_to_string(path) {
148 Ok(json) => {
149 let database: InMemory = serde_json::from_str(&json).map_err(|e| -> Error {
150 BackendError::DeserializationFailed { source: e }.into()
151 })?;
152 Ok(database)
153 }
154 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(InMemory::new()),
155 Err(e) => Err(BackendError::FileIo { source: e }.into()),
156 }
157}