oca_rs/
data_storage.rs

1use dyn_clonable::*;
2use std::{collections::HashMap, path::PathBuf};
3
4pub enum Namespace {
5    OCA,
6    OCABundlesJSON,
7    OCAObjectsJSON,
8    CoreModel,
9    OCARelations,
10    OCAReferences,
11}
12
13impl Namespace {
14    pub fn as_str(&self) -> &'static str {
15        match self {
16            Self::OCA => "oca",
17            Self::OCABundlesJSON => "oca_bundles_json",
18            Self::OCAObjectsJSON => "oca_objects_json",
19            Self::CoreModel => "core_model",
20            Self::OCARelations => "oca_relations",
21            Self::OCAReferences => "oca_refs",
22        }
23    }
24}
25
26#[clonable]
27pub trait DataStorage: Clone + Send {
28    fn get(&self, namespace: Namespace, key: &str) -> Result<Option<Vec<u8>>, String>;
29    fn get_all(&self, namespace: Namespace) -> Result<HashMap<String, Vec<u8>>, String>;
30    fn insert(&mut self, namespace: Namespace, key: &str, value: &[u8]) -> Result<(), String>;
31    fn new() -> Self
32    where
33        Self: Sized;
34    fn config(&self, config: HashMap<String, String>) -> Self
35    where
36        Self: Sized;
37    fn open(_path: &str) -> Self
38    where
39        Self: Sized,
40    {
41        panic!("DEPRECATED: use new() and config() instead of open()");
42    }
43}
44
45#[derive(Clone)]
46pub struct SledDataStorage {
47    db: Option<sled::Db>,
48}
49
50#[derive(Clone)]
51pub struct SledDataStorageConfig {
52    pub path: String,
53}
54
55impl SledDataStorageConfig {
56    pub fn build() -> SledDataStorageConfigBuilder {
57        SledDataStorageConfigBuilder { path: None }
58    }
59}
60
61pub struct SledDataStorageConfigBuilder {
62    path: Option<PathBuf>,
63}
64
65impl SledDataStorageConfigBuilder {
66    pub fn path(mut self, path: PathBuf) -> Self {
67        self.path = Some(path);
68        self
69    }
70
71    pub fn finalize(&self) -> Result<HashMap<String, String>, String> {
72        let mut config = HashMap::new();
73
74        match &self.path {
75            Some(path) => config.insert(
76                "path".to_string(),
77                path.clone()
78                    .into_os_string()
79                    .into_string()
80                    .map_err(|e| e.into_string().unwrap())?,
81            ),
82            None => return Err("path is required".to_string()),
83        };
84
85        Ok(config)
86    }
87
88    pub fn unwrap(&self) -> HashMap<String, String> {
89        self.finalize().unwrap()
90    }
91}
92
93impl DataStorage for SledDataStorage {
94    fn new() -> Self {
95        Self { db: None }
96    }
97
98    fn config(&self, config: HashMap<String, String>) -> Self {
99        if let Some(path) = config.get("path") {
100            if let Ok(db) = sled::open(path) {
101                return Self { db: Some(db) };
102            }
103        }
104        self.clone()
105    }
106
107    fn get(&self, namespace: Namespace, key: &str) -> Result<Option<Vec<u8>>, String> {
108        if let Some(ref db) = self.db {
109            let tree = db.open_tree(namespace.as_str().as_bytes()).unwrap();
110            match tree.get(key.as_bytes()).unwrap() {
111                Some(value) => Ok(Some(value.to_vec())),
112                None => Ok(None),
113            }
114        } else {
115            Err("Data Storage must be opened first".to_string())
116        }
117    }
118
119    fn get_all(&self, namespace: Namespace) -> Result<HashMap<String, Vec<u8>>, String> {
120        if let Some(ref db) = self.db {
121            let mut all = HashMap::new();
122            let tree = db.open_tree(namespace.as_str().as_bytes()).unwrap();
123            let mut iter = tree.iter();
124            while let Some(Ok((key, value))) = iter.next() {
125                all.insert(String::from_utf8(key.to_vec()).unwrap(), value.to_vec());
126            }
127
128            Ok(all)
129        } else {
130            Err("Data Storage must be opened first".to_string())
131        }
132    }
133
134    fn insert(&mut self, namespace: Namespace, key: &str, value: &[u8]) -> Result<(), String> {
135        if let Some(ref db) = self.db {
136            let tree = db.open_tree(namespace.as_str().as_bytes()).unwrap();
137            match tree.insert(key.as_bytes(), value) {
138                Ok(_) => Ok(()),
139                Err(e) => Err(e.to_string()),
140            }
141        } else {
142            Err("Data Storage must be opened first".to_string())
143        }
144    }
145}
146
147#[derive(Clone)]
148pub struct InMemoryDataStorage {
149    db: HashMap<String, HashMap<String, Vec<u8>>>,
150}
151
152impl DataStorage for InMemoryDataStorage {
153    fn new() -> Self {
154        Self { db: HashMap::new() }
155    }
156
157    fn config(&self, _config: HashMap<String, String>) -> Self {
158        self.clone()
159    }
160
161    fn get(&self, namespace: Namespace, key: &str) -> Result<Option<Vec<u8>>, String> {
162        let namespace_storage = match self.db.get(namespace.as_str()) {
163            Some(namespace_storage) => namespace_storage,
164            None => return Ok(None),
165        };
166        match namespace_storage.get(key) {
167            Some(value) => Ok(Some(value.to_vec())),
168            None => Ok(None),
169        }
170    }
171
172    fn get_all(&self, namespace: Namespace) -> Result<HashMap<String, Vec<u8>>, String> {
173        match self.db.get(namespace.as_str()) {
174            Some(namespace_storage) => Ok(namespace_storage.clone()),
175            None => Ok(HashMap::new()),
176        }
177    }
178
179    fn insert(&mut self, namespace: Namespace, key: &str, value: &[u8]) -> Result<(), String> {
180        let mut namespace_storage = match self.db.get(namespace.as_str()) {
181            Some(namespace_storage) => namespace_storage.clone(),
182            None => HashMap::new(),
183        };
184        namespace_storage.insert(key.to_string(), value.to_vec());
185        self.db
186            .insert(namespace.as_str().to_string(), namespace_storage);
187
188        Ok(())
189    }
190}
191
192#[derive(Clone)]
193pub struct FileSystemStorage {
194    dir: Option<PathBuf>,
195}
196
197#[derive(Clone)]
198pub struct FileSystemStorageConfig {
199    pub path: String,
200}
201
202impl FileSystemStorageConfig {
203    pub fn build() -> FileSystemStorageConfigBuilder {
204        FileSystemStorageConfigBuilder { path: None }
205    }
206}
207
208pub struct FileSystemStorageConfigBuilder {
209    path: Option<PathBuf>,
210}
211
212impl FileSystemStorageConfigBuilder {
213    pub fn path(mut self, path: PathBuf) -> Self {
214        self.path = Some(path);
215        self
216    }
217
218    pub fn finalize(&self) -> Result<HashMap<String, String>, String> {
219        let mut config = HashMap::new();
220
221        match &self.path {
222            Some(path) => config.insert(
223                "path".to_string(),
224                path.clone()
225                    .into_os_string()
226                    .into_string()
227                    .map_err(|e| e.into_string().unwrap())?,
228            ),
229            None => return Err("path is required".to_string()),
230        };
231
232        Ok(config)
233    }
234
235    pub fn unwrap(&self) -> HashMap<String, String> {
236        self.finalize().unwrap()
237    }
238}
239
240impl DataStorage for FileSystemStorage {
241    fn new() -> Self {
242        Self { dir: None }
243    }
244
245    fn config(&self, config: HashMap<String, String>) -> Self {
246        if let Some(path) = config.get("path") {
247            return Self {
248                dir: Some(PathBuf::from(path)),
249            };
250        }
251        self.clone()
252    }
253
254    fn get(&self, namespace: Namespace, key: &str) -> Result<Option<Vec<u8>>, String> {
255        if let Some(ref dir) = self.dir {
256            let mut path = dir.clone();
257            path.push(namespace.as_str());
258            if path.try_exists().unwrap() {
259                path.push(key);
260                Ok(std::fs::read(path.clone()).ok())
261            } else {
262                Ok(None)
263            }
264        } else {
265            Err("File path is required".to_string())
266        }
267    }
268
269    fn get_all(&self, _namespace: Namespace) -> Result<HashMap<String, Vec<u8>>, String> {
270        Err("Not implemented".to_string())
271    }
272
273    fn insert(&mut self, namespace: Namespace, key: &str, value: &[u8]) -> Result<(), String> {
274        if let Some(ref dir) = self.dir {
275            let mut path = dir.clone();
276            path.push(namespace.as_str());
277            if !path.try_exists().unwrap() {
278                std::fs::create_dir_all(path.clone()).unwrap();
279            }
280
281            path.push(key);
282            std::fs::write(path.clone(), value).map_err(|e| e.to_string())
283        } else {
284            Err("File path is required".to_string())
285        }
286    }
287}