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")]
181use redb::ReadableDatabase;
182
183#[cfg(feature = "redb")]
184const REDB_TABLE: redb::TableDefinition<'static, &[u8], &[u8]> =
185 redb::TableDefinition::new("entries");
186
187#[cfg(feature = "redb")]
195pub struct RedbStore {
196 db: redb::Database,
197}
198
199#[cfg(feature = "redb")]
200impl RedbStore {
201 #[allow(clippy::result_large_err)]
204 pub fn open(path: impl AsRef<std::path::Path>) -> Result<Self, redb::Error> {
205 let db = redb::Database::create(path)?;
206 let wtx = db.begin_write()?;
207 wtx.open_table(REDB_TABLE)?;
208 wtx.commit()?;
209 Ok(Self { db })
210 }
211
212 pub fn from_db(db: redb::Database) -> Self {
214 Self { db }
215 }
216}
217
218#[cfg(feature = "redb")]
219impl Store for RedbStore {
220 type Error = redb::Error;
221
222 fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
223 let rtx = self.db.begin_read()?;
224 let table = rtx.open_table(REDB_TABLE)?;
225 Ok(table.get(key)?.map(|g| g.value().to_vec()))
226 }
227
228 fn seek(&self, from: &[u8]) -> Result<Option<KeyValue>, Self::Error> {
229 let rtx = self.db.begin_read()?;
230 let table = rtx.open_table(REDB_TABLE)?;
231 match table.range::<&[u8]>(from..)?.next() {
232 Some(r) => {
233 let (k, v) = r?;
234 Ok(Some((k.value().to_vec(), v.value().to_vec())))
235 }
236 None => Ok(None),
237 }
238 }
239
240 fn seek_back(&self, upto: &[u8]) -> Result<Option<KeyValue>, Self::Error> {
241 let rtx = self.db.begin_read()?;
242 let table = rtx.open_table(REDB_TABLE)?;
243 match table.range::<&[u8]>(..=upto)?.next_back() {
244 Some(r) => {
245 let (k, v) = r?;
246 Ok(Some((k.value().to_vec(), v.value().to_vec())))
247 }
248 None => Ok(None),
249 }
250 }
251
252 fn commit(&self, ops: &[Op<'_>], durable: bool) -> Result<(), Self::Error> {
253 let mut wtx = self.db.begin_write()?;
254 if !durable {
255 wtx.set_durability(redb::Durability::None)?;
257 }
258 {
259 let mut table = wtx.open_table(REDB_TABLE)?;
260 for op in ops {
261 match op {
262 Op::Put(k, v) => {
263 table.insert(*k, *v)?;
264 }
265 Op::Delete(k) => {
266 table.remove(*k)?;
267 }
268 }
269 }
270 }
271 wtx.commit()?;
272 Ok(())
273 }
274}
275
276#[cfg(feature = "rocksdb")]
291pub struct RocksStore {
292 db: rocksdb::DB,
293}
294
295#[cfg(feature = "rocksdb")]
296impl RocksStore {
297 pub fn open(path: impl AsRef<std::path::Path>) -> Result<Self, rocksdb::Error> {
299 Ok(Self {
300 db: rocksdb::DB::open_default(path)?,
301 })
302 }
303
304 pub fn from_db(db: rocksdb::DB) -> Self {
306 Self { db }
307 }
308}
309
310#[cfg(feature = "rocksdb")]
311impl Store for RocksStore {
312 type Error = rocksdb::Error;
313
314 fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
315 self.db.get(key)
316 }
317
318 fn seek(&self, from: &[u8]) -> Result<Option<KeyValue>, Self::Error> {
319 let mut iter = self.db.iterator(rocksdb::IteratorMode::From(
320 from,
321 rocksdb::Direction::Forward,
322 ));
323 match iter.next() {
324 Some(Ok((k, v))) => Ok(Some((k.to_vec(), v.to_vec()))),
325 Some(Err(e)) => Err(e),
326 None => Ok(None),
327 }
328 }
329
330 fn seek_back(&self, upto: &[u8]) -> Result<Option<KeyValue>, Self::Error> {
331 let mut iter = self.db.iterator(rocksdb::IteratorMode::From(
332 upto,
333 rocksdb::Direction::Reverse,
334 ));
335 match iter.next() {
336 Some(Ok((k, v))) => Ok(Some((k.to_vec(), v.to_vec()))),
337 Some(Err(e)) => Err(e),
338 None => Ok(None),
339 }
340 }
341
342 fn commit(&self, ops: &[Op<'_>], durable: bool) -> Result<(), Self::Error> {
343 let mut batch = rocksdb::WriteBatch::default();
344 for op in ops {
345 match op {
346 Op::Put(k, v) => batch.put(*k, *v),
347 Op::Delete(k) => batch.delete(*k),
348 }
349 }
350 let mut opts = rocksdb::WriteOptions::default();
351 opts.set_sync(durable);
352 self.db.write_opt(batch, &opts)
353 }
354}
355
356#[cfg(test)]
357mod tests {
358 use super::*;
359
360 #[test]
361 fn mem_store_contract() {
362 contract(MemStore::new());
363 }
364
365 #[cfg(feature = "sled")]
366 #[test]
367 fn sled_store_contract() {
368 let dir = tempfile::tempdir().unwrap();
369 contract(SledStore::open(dir.path().join("db")).unwrap());
370 }
371
372 #[cfg(feature = "redb")]
373 #[test]
374 fn redb_store_contract() {
375 let dir = tempfile::tempdir().unwrap();
376 contract(RedbStore::open(dir.path().join("db.redb")).unwrap());
377 }
378
379 #[cfg(feature = "rocksdb")]
380 #[test]
381 fn rocksdb_store_contract() {
382 let dir = tempfile::tempdir().unwrap();
383 contract(RocksStore::open(dir.path().join("db")).unwrap());
384 }
385
386 fn contract<S: Store>(store: S) {
389 assert!(store.get(b"missing").unwrap().is_none());
390 assert!(store.seek(b"a").unwrap().is_none());
391
392 store
393 .commit(
394 &[
395 Op::Put(b"b", b"2"),
396 Op::Put(b"a", b"1"),
397 Op::Put(b"c", b"3"),
398 ],
399 true,
400 )
401 .unwrap();
402
403 assert_eq!(store.get(b"a").unwrap().as_deref(), Some(&b"1"[..]));
404 assert_eq!(store.get(b"z").unwrap(), None);
405
406 let (k, v) = store.seek(b"a").unwrap().unwrap();
407 assert_eq!((k.as_slice(), v.as_slice()), (&b"a"[..], &b"1"[..]));
408 assert_eq!(store.seek(b"aa").unwrap().unwrap().0.as_slice(), b"b");
409 assert_eq!(store.seek_back(b"bz").unwrap().unwrap().0.as_slice(), b"b");
410 assert_eq!(
411 store.seek_back(b"\xff").unwrap().unwrap().0.as_slice(),
412 b"c"
413 );
414
415 store.commit(&[Op::Delete(b"b")], true).unwrap();
416 assert_eq!(store.get(b"b").unwrap(), None);
417 assert_eq!(store.seek(b"b").unwrap().unwrap().0.as_slice(), b"c");
418 }
419}