hexroll3_scroll/
repository.rs1use anyhow::{anyhow, Result};
26use redb::{ReadableTable, Savepoint};
27use serde::{Deserialize, Serialize};
28use std::{
29 cell::RefCell,
30 collections::HashMap,
31 sync::{Arc, Mutex},
32};
33
34pub struct Repository {
35 pub db: Option<Arc<Mutex<redb::Database>>>,
36}
37
38impl Repository {
39 pub fn new() -> Self {
40 Repository { db: None }
41 }
42
43 pub fn create(&mut self, filename: &str) -> Result<&mut Self> {
44 self.db = Some(Arc::new(Mutex::new(redb::Database::create(filename)?)));
45 Ok(self)
46 }
47
48 pub fn open(&mut self, filename: &str) -> Result<&mut Self> {
49 self.db = Some(Arc::new(Mutex::new(redb::Database::open(filename)?)));
50 Ok(self)
51 }
52
53 pub fn load(&self, uid: &str) -> Result<serde_json::Value> {
54 let tx = self
55 .db
56 .as_ref()
57 .ok_or_else(|| anyhow!("Database reference is missing"))?
58 .lock()
59 .map_err(|_| anyhow!("Failed to acquire lock"))?
60 .begin_read()
61 .map_err(|_| anyhow!("Failed to begin read transaction"))?;
62
63 const TABLE: redb::TableDefinition<String, JsonValue> =
64 redb::TableDefinition::new("my_data2");
65
66 let table = tx
67 .open_table(TABLE)
68 .map_err(|_| anyhow!("Failed to open table"))?;
69
70 match table.get(uid.to_string()) {
71 Ok(Some(ret)) => Ok(ret.value().value),
72 Ok(None) => Err(anyhow!("No entry found for uid: {}", uid)),
73 Err(_) => Err(anyhow!("Error retrieving entry for uid: {}", uid)),
74 }
75 }
76
77 pub fn mutate<F, R>(&self, mut f: F) -> Result<R>
78 where
79 F: FnMut(&mut ReadWriteTransaction) -> Result<R>,
80 {
81 let tx = self
82 .db
83 .as_ref()
84 .ok_or_else(|| anyhow!("Database not initialized"))?
85 .lock()
86 .map_err(|_| anyhow!("Failed to acquire lock"))?
87 .begin_write()
88 .map_err(|_| anyhow!("Failed to begin write transaction"))?;
89 const TABLE: redb::TableDefinition<String, JsonValue> =
90 redb::TableDefinition::new("my_data2");
91
92 let closure_result = {
93 let table = tx.open_table(TABLE)?;
94 let mut repo_tx = ReadWriteTransaction {
95 cache: HashMap::new(),
96 table,
97 };
98 f(&mut repo_tx)?
99 };
100
101 tx.commit()
102 .map_err(|_| anyhow!("Failed to commit transaction"))?;
103 Ok(closure_result)
104 }
105
106 pub fn savepoint(&self) -> Result<Savepoint> {
107 let tx = self
108 .db
109 .as_ref()
110 .ok_or_else(|| anyhow!("Database not initialized"))?
111 .lock()
112 .map_err(|_| anyhow!("Failed to acquire lock"))?
113 .begin_write()
114 .map_err(|_| anyhow!("Failed to begin write transaction"))?;
115 Ok(tx.ephemeral_savepoint()?)
116 }
117
118 pub fn restore(&self, savepoint: &Savepoint) -> Result<()> {
119 let mut tx = self
120 .db
121 .as_ref()
122 .ok_or_else(|| anyhow!("Database not initialized"))?
123 .lock()
124 .map_err(|_| anyhow!("Failed to acquire lock"))?
125 .begin_write()
126 .map_err(|_| anyhow!("Failed to begin write transaction"))?;
127 tx.restore_savepoint(savepoint)?;
128 Ok(tx.commit()?)
129 }
130
131 pub fn inspect<F, R>(&self, f: F) -> Result<R>
132 where
133 F: FnMut(&mut ReadOnlyTransaction) -> Result<R>,
134 {
135 let f = RefCell::new(f);
136 let tx = self
137 .db
138 .as_ref()
139 .ok_or_else(|| anyhow!("Database reference is missing"))?
140 .lock()
141 .map_err(|_| anyhow!("Failed to acquire database lock"))?
142 .begin_read()
143 .map_err(|_| anyhow!("Failed to begin read transaction"))?;
144 const TABLE: redb::TableDefinition<String, JsonValue> =
145 redb::TableDefinition::new("my_data2");
146 let closure_result: R;
147 {
148 let table = tx.open_table(TABLE)?;
149 let mut repo_tx = ReadOnlyTransaction {
150 cache: HashMap::new(),
151 table,
152 };
153 closure_result = f.borrow_mut()(&mut repo_tx)?;
154 }
155 Ok(closure_result)
156 }
157}
158
159impl Default for Repository {
160 fn default() -> Self {
161 Self::new()
162 }
163}
164
165pub trait ReadOnlyLoader {
166 fn retrieve(&self, uid: &str) -> Result<JsonValue>;
167}
168
169pub struct ReadWriteTransaction<'a> {
170 cache: HashMap<String, serde_json::Value>,
171 pub table: redb::Table<'a, String, JsonValue>,
172}
173
174pub struct ReadOnlyTransaction {
175 pub cache: HashMap<String, serde_json::Value>,
176 pub table: redb::ReadOnlyTable<String, JsonValue>,
177}
178
179impl<'a> ReadOnlyLoader for ReadWriteTransaction<'a> {
180 fn retrieve(&self, uid: &str) -> Result<JsonValue> {
181 if let Some(cached) = self.cache.get(uid) {
182 Ok(JsonValue {
183 value: cached.clone(),
184 })
185 } else if let Ok(Some(ret)) = self.table.get(uid.to_string()) {
186 Ok(ret.value())
187 } else {
188 Err(anyhow!("error in loading {}", uid))
189 }
190 }
191}
192
193impl<'a> ReadWriteTransaction<'a> {
194 pub fn _has_cache(&mut self, uid: &str) -> bool {
195 self.cache.contains_key(uid)
196 }
197 pub fn create(&mut self, uid: &str) -> Result<&mut serde_json::Value> {
198 if !(self.cache.contains_key(uid)) {
199 self.cache
200 .insert(uid.to_string(), serde_json::json!({"uid": uid}));
201 }
202 Ok(self.cache.get_mut(uid).unwrap())
203 }
204 pub fn load(&mut self, uid: &str) -> Result<&mut serde_json::Value> {
205 if !(self.cache.contains_key(uid)) {
206 self.cache
207 .insert(uid.to_string(), self.retrieve(uid)?.value);
208 }
209 Ok(self.cache.get_mut(uid).unwrap())
210 }
211 pub fn store(&mut self, uid: &str, value: &serde_json::Value) -> Result<()> {
212 self.table
213 .insert(
214 uid.to_string(),
215 JsonValue {
216 value: value.clone(),
217 },
218 )
219 .map_err(|e| anyhow!(e))?;
220 Ok(())
221 }
222 pub fn save(&mut self, uid: &str) -> Result<()> {
223 if let Some(e) = self.cache.get(uid) {
224 self.table
225 .insert(uid.to_string(), &JsonValue { value: e.clone() })
226 .map_err(|e| anyhow!(e))?;
227 Ok(())
228 } else {
229 Err(anyhow!("Entity not found in cache"))
230 }
231 }
232 pub fn remove(&mut self, uid: &str) -> Result<()> {
233 self.table.remove(uid.to_string())?;
234 if self.cache.contains_key(uid) {
235 self.cache.remove(uid);
236 }
237 Ok(())
238 }
239 pub fn emplace_and_save(&mut self, uid: &str, v: serde_json::Value) -> Result<()> {
240 self.cache.insert(uid.to_string(), v);
241 self.save(uid)
242 }
243}
244
245impl ReadOnlyLoader for ReadOnlyTransaction {
246 fn retrieve(&self, uid: &str) -> Result<JsonValue> {
247 if let Some(cached) = self.cache.get(uid) {
248 Ok(JsonValue {
249 value: cached.clone(),
250 })
251 } else if let Ok(Some(ret)) = self.table.get(uid.to_string()) {
252 Ok(ret.value())
253 } else {
254 Err(anyhow!("error in loading {}", uid))
255 }
256 }
257}
258
259impl ReadOnlyTransaction {
260 pub fn fetch(&mut self, uid: &str) -> Result<&mut serde_json::Value> {
261 if !(self.cache.contains_key(uid)) {
262 self.cache
263 .insert(uid.to_string(), self.retrieve(uid)?.value);
264 }
265 Ok(self.cache.get_mut(uid).unwrap())
266 }
267 pub fn load(&self, uid: &str) -> Result<JsonValue> {
268 if let Some(cached) = self.cache.get(uid) {
269 Ok(JsonValue {
270 value: cached.clone(),
271 })
272 } else if let Ok(Some(ret)) = self.table.get(uid.to_string()) {
273 Ok(ret.value())
274 } else {
275 Err(anyhow!("error in loading {}", uid))
276 }
277 }
278}
279
280pub trait Entity {
281 fn values(&self) -> &serde_json::Value;
282 fn values_mut(&mut self) -> &mut serde_json::Value;
283 fn is_missing(&self, attr: &str) -> bool {
284 !self.values().as_object().unwrap().contains_key(attr)
285 }
286 fn clear(&mut self, attr: &str) {
287 self.values_mut().as_object_mut().unwrap().swap_remove(attr);
288 }
289}
290
291impl Entity for serde_json::Value {
292 fn values(&self) -> &serde_json::Value {
293 self
294 }
295 fn values_mut(&mut self) -> &mut serde_json::Value {
296 self
297 }
298}
299
300fn json_bytes<T>(structure: T) -> Vec<u8>
301where
302 T: serde::Serialize,
303{
304 let mut bytes: Vec<u8> = Vec::new();
305 ciborium::into_writer(&structure, &mut bytes).unwrap();
306 #[cfg(feature = "zstd")]
307 {
308 zstd::encode_all(bytes.as_slice(), 0).unwrap()
309 }
310 #[cfg(not(feature = "zstd"))]
311 {
312 bytes
313 }
314}
315
316fn bytes_json(bytes: &[u8]) -> serde_json::Value {
317 #[cfg(feature = "zstd")]
318 let cbor = {
319 let bytes = zstd::decode_all(bytes).unwrap();
320 ciborium::from_reader(bytes.as_slice()).unwrap();
321 };
322
323 #[cfg(not(feature = "zstd"))]
324 let cbor: ciborium::Value = ciborium::from_reader(bytes).unwrap();
325
326 serde_json::to_value(cbor).unwrap()
327}
328
329#[derive(Debug, Serialize, Deserialize)]
330pub struct JsonValue {
331 pub value: serde_json::Value,
332}
333
334impl redb::Value for JsonValue {
335 type SelfType<'a> = JsonValue
336 where
337 Self: 'a;
338 type AsBytes<'a> = Vec<u8>
339 where
340 Self: 'a;
341
342 fn fixed_width() -> Option<usize> {
343 None
344 }
345
346 fn from_bytes<'a>(data: &'a [u8]) -> JsonValue
347 where
348 Self: 'a,
349 {
350 JsonValue {
351 value: bytes_json(data),
352 }
353 }
354
355 fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a>
356 where
357 Self: 'a,
358 Self: 'b,
359 {
360 json_bytes(&value.value)
361 }
362
363 fn type_name() -> redb::TypeName {
364 redb::TypeName::new("test::JsonValue")
365 }
366}