gen_memory_database/
persistence.rs1use std::collections::BTreeSet;
2use std::io;
3use convert_case::Case;
4use convert_case::Casing;
5use gen_memory::bucket::Bucket;
6use gen_memory::store;
7use serde::Deserialize;
8use serde::Serialize;
9use uuid::Uuid;
10use crate::index;
11use crate::temporary;
12
13pub fn init_db_store(store: &Store) -> io::Result<()> {
14 store::remove_if_exists(&store.bucket)?;
15 store::create(&store.bucket)?;
16 Ok(())
17}
18
19pub fn delete_db_store(store: &Store) -> io::Result<()> {
20 store::remove_if_exists(&store.bucket)?;
21 Ok(())
22}
23
24#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
25pub struct Store {
26 pub bucket: Bucket,
27}
28
29impl Store {
30 pub fn from_bucket(bucket: &Bucket) -> Store {
31 Store {
32 bucket: bucket.clone()
33 }
34 }
35
36 pub fn from(name: &str) -> io::Result<Store> {
37 Ok(Store {
38 bucket: Bucket::try_from(name)?
39 })
40 }
41}
42
43#[derive(Debug, Clone, PartialEq, Hash, Eq, Serialize, Deserialize)]
44pub struct Capsule<T> {
45 pub inner: T,
46 pub id: Uuid,
47 pub key: String,
48 pub store: Store,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct Persist<T> {
53 pub inner: T,
54}
55
56pub trait Indexable {
57 fn index_keys(&self) -> std::collections::BTreeSet<String>;
58}
59
60pub trait FileDbKey {
61 fn file_db_key() -> String;
62}
63
64pub fn save_capsule<T: Serialize + Clone + for<'a> Deserialize<'a> + FileDbKey>(capsule: Capsule<T>, index_keys: BTreeSet<String>) -> io::Result<Capsule<T>> {
65 if let Some(id) = if !index_keys.is_empty()
66 { index::index(index_keys, &capsule.key, &capsule.id, &capsule.store)? } else { None } {
67 Capsule::<T>::get_by_id(&id, &capsule.store)
68 } else {
69 temporary::save(capsule)
70 }
71}
72
73pub fn upsert_capsule<T: Serialize + Clone + for<'a> Deserialize<'a> + FileDbKey>(capsule: Capsule<T>, index_keys: BTreeSet<String>) -> io::Result<Capsule<T>> {
74 if let Some(id) = if !index_keys.is_empty()
75 { index::index(index_keys, &capsule.key, &capsule.id, &capsule.store)? } else { None } {
76 let mut found = Capsule::<T>::get_by_id(&id, &capsule.store)?;
77 found.inner = capsule.inner;
78 temporary::save(found)
79 } else {
80 temporary::save(capsule)
81 }
82}
83
84pub fn save_object<T: Serialize + Clone + FileDbKey>(data: &T, store: &Store) -> io::Result<T> {
85 temporary::create(store, &T::file_db_key().to_case(Case::Snake), data)?;
86 Ok(data.clone())
87}
88
89
90impl <T: Serialize + for<'a> Deserialize<'a> + Clone + FileDbKey> Capsule<T> {
91 pub fn update(&self) -> io::Result<Capsule<T>>
92 where
93 T: Indexable,
94 {
95 let capsule = self.clone();
96 if !self.inner.index_keys().is_empty() {
97 let _ = index::update(self.inner.index_keys(), &capsule.key, &capsule.id, &capsule.store);
98 }
99 temporary::save(capsule)
100 }
101
102
103 pub fn get_by_id(id: &Uuid, store: &Store) -> io::Result<Capsule<T>> {
104 temporary::select::<Capsule<T>>(store, &format!("{}_{}", T::file_db_key().to_case(Case::Snake), id))
105 .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, format!("cannot find {} {} in database", T::file_db_key(), id)))
106 }
107
108 pub fn get_all(store: &Store) -> io::Result<Vec<Capsule<T>>> {
109 let id_vec = index::get_all_by_type(&T::file_db_key().to_case(Case::Snake), store)?;
110
111 Ok(id_vec
112 .into_iter()
113 .flat_map(|id| Capsule::<T>::get_by_id(&id, store))
114 .collect())
115 }
116
117 pub fn find_by_index(keys: &[&str], store: &Store) -> io::Result<Vec<Capsule<T>>> {
118 let id_vec = index::find_by_type(&T::file_db_key().to_case(Case::Snake), keys, store)?;
119
120 Ok(id_vec
121 .into_iter()
122 .flat_map(|id| Capsule::<T>::get_by_id(&id, store))
123 .collect())
124 }
125}
126
127impl <T: Serialize + for<'a> Deserialize<'a> + Clone + FileDbKey> Persist<T> {
128 pub fn get(store: &Store) -> io::Result<T> {
129 temporary::select::<T>(store, &T::file_db_key().to_case(Case::Snake))
130 .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, format!("cannot find {} in database", T::file_db_key())))
131 }
132}
133
134#[cfg(test)]
135#[path = "../tests/unit_tests/persistence.rs"]
136pub mod test;
137