dinglebit_store/
collection.rs1use std::marker::PhantomData;
4use std::str::from_utf8;
5
6use serde::{de::DeserializeOwned, Serialize};
7use serde_json::{from_str, to_string};
8
9type Result<T> = std::result::Result<T, Error>;
10
11#[derive(Debug)]
13pub enum Error {
14 SystemError(String),
16
17 Serialization(String),
19
20 PermissionDenied,
22}
23
24pub trait Collection<'a, T>
27where
28 T: Serialize + DeserializeOwned + 'a,
29{
30 fn put(&mut self, key: &str, value: T) -> Result<Option<T>>;
34
35 fn get(&mut self, key: &str) -> Result<Option<T>>;
37
38 fn prefix(&mut self, prefix: &str) -> Box<dyn Iterator<Item = Result<T>> + 'a>;
41}
42
43pub struct Sled {
45 db: sled::Db,
46}
47
48impl Sled {
49 pub fn open(file: &str) -> Result<Self> {
51 let db = match sled::open(file) {
52 Ok(db) => db,
53 Err(e) => return Err(Error::SystemError(e.to_string())),
54 };
55
56 Ok(Self { db: db })
57 }
58}
59
60impl<'a, T> Collection<'a, T> for Sled
61where
62 T: Serialize + DeserializeOwned + 'a,
63{
64 fn put(&mut self, key: &str, value: T) -> Result<Option<T>> {
65 let json = match to_string(&value) {
67 Ok(str) => str,
68 Err(err) => return Err(Error::Serialization(err.to_string())),
69 };
70
71 let value = match self.db.insert(key.as_bytes(), json.as_bytes()) {
73 Ok(value) => match value {
74 Some(value) => value,
75 None => return Ok(None),
76 },
77 Err(err) => return Err(Error::SystemError(err.to_string())),
78 };
79
80 let json = match from_utf8(&value) {
83 Ok(json) => json,
84 Err(err) => return Err(Error::Serialization(err.to_string())),
85 };
86 let obj: T = match from_str(json) {
87 Ok(json) => json,
88 Err(err) => return Err(Error::Serialization(err.to_string())),
89 };
90
91 Ok(Some(obj))
92 }
93
94 fn get(&mut self, key: &str) -> Result<Option<T>> {
95 let ivec = match self.db.get(key.as_bytes()) {
97 Err(err) => return Err(Error::SystemError(err.to_string())),
98 Ok(ivec) => match ivec {
99 Some(ivec) => ivec,
100 None => return Ok(None),
101 },
102 };
103
104 let json = match from_utf8(&ivec) {
106 Ok(json) => json,
107 Err(err) => return Err(Error::Serialization(err.to_string())),
108 };
109 let json: T = match from_str(json) {
110 Ok(json) => json,
111 Err(err) => return Err(Error::Serialization(err.to_string())),
112 };
113
114 Ok(Some(json))
115 }
116
117 fn prefix(&mut self, prefix: &str) -> Box<dyn Iterator<Item = Result<T>> + 'a> {
118 Box::new(SledIter {
119 p: PhantomData,
120 iter: self.db.scan_prefix(prefix.as_bytes()),
121 })
122 }
123}
124
125pub struct SledIter<T> {
127 p: PhantomData<T>,
128 iter: sled::Iter,
129}
130
131impl<T> Iterator for SledIter<T>
132where
133 T: DeserializeOwned,
134{
135 type Item = Result<T>;
136 fn next(&mut self) -> Option<Self::Item> {
137 let data = match self.iter.next() {
139 Some(r) => match r {
140 Ok(data) => data,
141 Err(e) => return Some(Err(Error::SystemError(e.to_string()))),
142 },
143 None => return None,
144 };
145
146 let json = match from_utf8(&(data.1)) {
148 Ok(json) => json,
149 Err(err) => return Some(Err(Error::Serialization(err.to_string()))),
150 };
151 let json: T = match from_str(json) {
152 Ok(json) => json,
153 Err(err) => return Some(Err(Error::Serialization(err.to_string()))),
154 };
155 Some(Ok(json))
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use crate::collection::*;
162 use serde::{Deserialize, Serialize};
163 use tempfile::tempdir;
164
165 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
166 struct Person {
167 name: String,
168 }
169
170 #[test]
171 fn collection_get_put() {
172 let file = tempdir().expect("unable to make temp file");
173 let mut s: Box<dyn Collection<'static, Person>> =
174 Box::new(Sled::open(file.path().to_str().unwrap()).expect("failed to open with sled"));
175
176 let p = Person {
177 name: "foo".to_string(),
178 };
179 s.put(p.name.as_str(), p.clone())
180 .expect("failed to put foo");
181
182 let r = s.get(p.name.as_str()).expect("failed to get foo").unwrap();
183 assert_eq!(p, r);
184
185 assert_eq!(
186 s.get("i don't exist").expect("failed to get non-existant"),
187 None
188 );
189 }
190
191 #[test]
192 fn collection_prefix() {
193 let file = tempdir().expect("unable to make temp file");
194 let mut s: Box<dyn Collection<'static, Person>> =
195 Box::new(Sled::open(file.path().to_str().unwrap()).expect("failed to open with sled"));
196
197 let pp = vec![
198 Person {
199 name: "foo".to_string(),
200 },
201 Person {
202 name: "bar/foo0".to_string(),
203 },
204 Person {
205 name: "bar/foo1".to_string(),
206 },
207 Person {
208 name: "bar/foo2".to_string(),
209 },
210 Person {
211 name: "baz".to_string(),
212 },
213 ];
214
215 for p in pp {
216 s.put(p.name.as_str(), p.clone()).expect("failed to put");
217 }
218
219 let rr = s
220 .prefix("bar/")
221 .map(|r| r.expect("collecting"))
222 .collect::<Vec<Person>>();
223
224 let exp = vec![
225 Person {
226 name: "bar/foo0".to_string(),
227 },
228 Person {
229 name: "bar/foo1".to_string(),
230 },
231 Person {
232 name: "bar/foo2".to_string(),
233 },
234 ];
235
236 assert_eq!(rr, exp);
237 }
238}