1use std::error::Error as StdError;
4
5pub enum Op<'a> {
7 Put(&'a [u8], &'a [u8]),
9 Delete(&'a [u8]),
11}
12
13pub type KeyValue = (Vec<u8>, Vec<u8>);
15
16pub trait Store: Send + Sync {
22 type Error: StdError + Send + Sync + 'static;
24
25 fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error>;
27
28 fn seek(&self, from: &[u8]) -> Result<Option<KeyValue>, Self::Error>;
30
31 fn seek_back(&self, upto: &[u8]) -> Result<Option<KeyValue>, Self::Error>;
33
34 fn commit(&self, ops: &[Op<'_>], durable: bool) -> Result<(), Self::Error>;
36}
37
38pub struct MemStore {
43 map: crate::sync::Mutex<std::collections::BTreeMap<Vec<u8>, Vec<u8>>>,
44}
45
46impl MemStore {
47 pub fn new() -> Self {
49 Self {
50 map: crate::sync::Mutex::new(std::collections::BTreeMap::new()),
51 }
52 }
53}
54
55impl Default for MemStore {
56 fn default() -> Self {
57 Self::new()
58 }
59}
60
61impl Store for MemStore {
62 type Error = std::convert::Infallible;
63
64 fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
65 Ok(self.map.lock().unwrap().get(key).cloned())
66 }
67
68 fn seek(&self, from: &[u8]) -> Result<Option<KeyValue>, Self::Error> {
69 Ok(self
70 .map
71 .lock()
72 .unwrap()
73 .range(from.to_vec()..)
74 .next()
75 .map(|(k, v)| (k.clone(), v.clone())))
76 }
77
78 fn seek_back(&self, upto: &[u8]) -> Result<Option<KeyValue>, Self::Error> {
79 Ok(self
80 .map
81 .lock()
82 .unwrap()
83 .range(..=upto.to_vec())
84 .next_back()
85 .map(|(k, v)| (k.clone(), v.clone())))
86 }
87
88 fn commit(&self, ops: &[Op<'_>], _durable: bool) -> Result<(), Self::Error> {
89 let mut map = self.map.lock().unwrap();
90 for op in ops {
91 match op {
92 Op::Put(k, v) => {
93 map.insert(k.to_vec(), v.to_vec());
94 }
95 Op::Delete(k) => {
96 map.remove(*k);
97 }
98 }
99 }
100 Ok(())
101 }
102}
103
104#[cfg(feature = "sled")]
114pub struct SledStore {
115 db: sled::Db,
116}
117
118#[cfg(feature = "sled")]
119impl SledStore {
120 pub fn open(path: impl AsRef<std::path::Path>) -> sled::Result<Self> {
122 Ok(Self {
123 db: sled::open(path)?,
124 })
125 }
126
127 pub fn from_db(db: sled::Db) -> Self {
129 Self { db }
130 }
131}
132
133#[cfg(feature = "sled")]
134impl Store for SledStore {
135 type Error = sled::Error;
136
137 fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
138 Ok(self.db.get(key)?.map(|v| v.to_vec()))
139 }
140
141 fn seek(&self, from: &[u8]) -> Result<Option<KeyValue>, Self::Error> {
142 match self.db.range(from.to_vec()..).next() {
143 Some(r) => {
144 let (k, v) = r?;
145 Ok(Some((k.to_vec(), v.to_vec())))
146 }
147 None => Ok(None),
148 }
149 }
150
151 fn seek_back(&self, upto: &[u8]) -> Result<Option<KeyValue>, Self::Error> {
152 match self.db.range(..=upto.to_vec()).next_back() {
153 Some(r) => {
154 let (k, v) = r?;
155 Ok(Some((k.to_vec(), v.to_vec())))
156 }
157 None => Ok(None),
158 }
159 }
160
161 fn commit(&self, ops: &[Op<'_>], durable: bool) -> Result<(), Self::Error> {
162 let mut batch = sled::Batch::default();
163 for op in ops {
164 match op {
165 Op::Put(k, v) => batch.insert(*k, *v),
166 Op::Delete(k) => batch.remove(*k),
167 }
168 }
169 self.db.apply_batch(batch)?;
170 if durable {
171 self.db.flush()?;
172 }
173 Ok(())
174 }
175}
176
177#[cfg(feature = "redb")]
180const REDB_TABLE: redb::TableDefinition<'static, &[u8], &[u8]> =
181 redb::TableDefinition::new("entries");
182
183#[cfg(feature = "redb")]
191pub struct RedbStore {
192 db: redb::Database,
193}
194
195#[cfg(feature = "redb")]
196impl RedbStore {
197 #[allow(clippy::result_large_err)]
200 pub fn open(path: impl AsRef<std::path::Path>) -> Result<Self, redb::Error> {
201 let db = redb::Database::create(path)?;
202 let wtx = db.begin_write()?;
203 wtx.open_table(REDB_TABLE)?;
204 wtx.commit()?;
205 Ok(Self { db })
206 }
207
208 pub fn from_db(db: redb::Database) -> Self {
210 Self { db }
211 }
212}
213
214#[cfg(feature = "redb")]
215impl Store for RedbStore {
216 type Error = redb::Error;
217
218 fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
219 let rtx = self.db.begin_read()?;
220 let table = rtx.open_table(REDB_TABLE)?;
221 Ok(table.get(key)?.map(|g| g.value().to_vec()))
222 }
223
224 fn seek(&self, from: &[u8]) -> Result<Option<KeyValue>, Self::Error> {
225 let rtx = self.db.begin_read()?;
226 let table = rtx.open_table(REDB_TABLE)?;
227 match table.range::<&[u8]>(from..)?.next() {
228 Some(r) => {
229 let (k, v) = r?;
230 Ok(Some((k.value().to_vec(), v.value().to_vec())))
231 }
232 None => Ok(None),
233 }
234 }
235
236 fn seek_back(&self, upto: &[u8]) -> Result<Option<KeyValue>, Self::Error> {
237 let rtx = self.db.begin_read()?;
238 let table = rtx.open_table(REDB_TABLE)?;
239 match table.range::<&[u8]>(..=upto)?.next_back() {
240 Some(r) => {
241 let (k, v) = r?;
242 Ok(Some((k.value().to_vec(), v.value().to_vec())))
243 }
244 None => Ok(None),
245 }
246 }
247
248 fn commit(&self, ops: &[Op<'_>], durable: bool) -> Result<(), Self::Error> {
249 let mut wtx = self.db.begin_write()?;
250 if !durable {
251 wtx.set_durability(redb::Durability::None);
252 }
253 {
254 let mut table = wtx.open_table(REDB_TABLE)?;
255 for op in ops {
256 match op {
257 Op::Put(k, v) => {
258 table.insert(*k, *v)?;
259 }
260 Op::Delete(k) => {
261 table.remove(*k)?;
262 }
263 }
264 }
265 }
266 wtx.commit()?;
267 Ok(())
268 }
269}
270
271#[cfg(feature = "rocksdb")]
286pub struct RocksStore {
287 db: rocksdb::DB,
288}
289
290#[cfg(feature = "rocksdb")]
291impl RocksStore {
292 pub fn open(path: impl AsRef<std::path::Path>) -> Result<Self, rocksdb::Error> {
294 Ok(Self {
295 db: rocksdb::DB::open_default(path)?,
296 })
297 }
298
299 pub fn from_db(db: rocksdb::DB) -> Self {
301 Self { db }
302 }
303}
304
305#[cfg(feature = "rocksdb")]
306impl Store for RocksStore {
307 type Error = rocksdb::Error;
308
309 fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
310 self.db.get(key)
311 }
312
313 fn seek(&self, from: &[u8]) -> Result<Option<KeyValue>, Self::Error> {
314 let mut iter = self.db.iterator(rocksdb::IteratorMode::From(
315 from,
316 rocksdb::Direction::Forward,
317 ));
318 match iter.next() {
319 Some(Ok((k, v))) => Ok(Some((k.to_vec(), v.to_vec()))),
320 Some(Err(e)) => Err(e),
321 None => Ok(None),
322 }
323 }
324
325 fn seek_back(&self, upto: &[u8]) -> Result<Option<KeyValue>, Self::Error> {
326 let mut iter = self.db.iterator(rocksdb::IteratorMode::From(
327 upto,
328 rocksdb::Direction::Reverse,
329 ));
330 match iter.next() {
331 Some(Ok((k, v))) => Ok(Some((k.to_vec(), v.to_vec()))),
332 Some(Err(e)) => Err(e),
333 None => Ok(None),
334 }
335 }
336
337 fn commit(&self, ops: &[Op<'_>], durable: bool) -> Result<(), Self::Error> {
338 let mut batch = rocksdb::WriteBatch::default();
339 for op in ops {
340 match op {
341 Op::Put(k, v) => batch.put(*k, *v),
342 Op::Delete(k) => batch.delete(*k),
343 }
344 }
345 let mut opts = rocksdb::WriteOptions::default();
346 opts.set_sync(durable);
347 self.db.write_opt(batch, &opts)
348 }
349}
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354
355 #[test]
356 fn mem_store_contract() {
357 contract(MemStore::new());
358 }
359
360 #[cfg(feature = "sled")]
361 #[test]
362 fn sled_store_contract() {
363 let dir = tempfile::tempdir().unwrap();
364 contract(SledStore::open(dir.path().join("db")).unwrap());
365 }
366
367 #[cfg(feature = "redb")]
368 #[test]
369 fn redb_store_contract() {
370 let dir = tempfile::tempdir().unwrap();
371 contract(RedbStore::open(dir.path().join("db.redb")).unwrap());
372 }
373
374 #[cfg(feature = "rocksdb")]
375 #[test]
376 fn rocksdb_store_contract() {
377 let dir = tempfile::tempdir().unwrap();
378 contract(RocksStore::open(dir.path().join("db")).unwrap());
379 }
380
381 fn contract<S: Store>(store: S) {
384 assert!(store.get(b"missing").unwrap().is_none());
385 assert!(store.seek(b"a").unwrap().is_none());
386
387 store
388 .commit(
389 &[
390 Op::Put(b"b", b"2"),
391 Op::Put(b"a", b"1"),
392 Op::Put(b"c", b"3"),
393 ],
394 true,
395 )
396 .unwrap();
397
398 assert_eq!(store.get(b"a").unwrap().as_deref(), Some(&b"1"[..]));
399 assert_eq!(store.get(b"z").unwrap(), None);
400
401 let (k, v) = store.seek(b"a").unwrap().unwrap();
402 assert_eq!((k.as_slice(), v.as_slice()), (&b"a"[..], &b"1"[..]));
403 assert_eq!(store.seek(b"aa").unwrap().unwrap().0.as_slice(), b"b");
404 assert_eq!(store.seek_back(b"bz").unwrap().unwrap().0.as_slice(), b"b");
405 assert_eq!(
406 store.seek_back(b"\xff").unwrap().unwrap().0.as_slice(),
407 b"c"
408 );
409
410 store.commit(&[Op::Delete(b"b")], true).unwrap();
411 assert_eq!(store.get(b"b").unwrap(), None);
412 assert_eq!(store.seek(b"b").unwrap().unwrap().0.as_slice(), b"c");
413 }
414}