pub mod lmdb_adapter; pub mod sled_adapter; pub mod sqlite_adapter; #[cfg(test)] pub mod test; use core::ops::{Bound, RangeBounds}; use std::borrow::Cow; use std::cell::Cell; use std::sync::Arc; use err_derive::Error; #[derive(Clone)] pub struct Db(pub(crate) Arc); pub struct Transaction<'a>(pub(crate) &'a mut dyn ITx); #[derive(Clone)] pub struct Tree(pub(crate) Arc, pub(crate) usize); pub type ValueIter<'a> = Box, Value<'a>)>> + 'a>; // ---- pub struct Value<'a>(pub(crate) Box + 'a>); pub trait IValue<'a>: AsRef<[u8]> + core::borrow::Borrow<[u8]> { fn take_maybe(&mut self) -> Vec; } impl<'a> Value<'a> { #[inline] pub fn into_vec(mut self) -> Vec { self.0.take_maybe() } } impl<'a> AsRef<[u8]> for Value<'a> { #[inline] fn as_ref(&self) -> &[u8] { self.0.as_ref().as_ref() } } impl<'a> std::borrow::Borrow<[u8]> for Value<'a> { #[inline] fn borrow(&self) -> &[u8] { self.0.as_ref().borrow() } } impl<'a> core::ops::Deref for Value<'a> { type Target = [u8]; #[inline] fn deref(&self) -> &[u8] { self.0.as_ref().as_ref() } } impl<'a, T> PartialEq for Value<'a> where T: AsRef<[u8]>, { fn eq(&self, other: &T) -> bool { self.as_ref() == other.as_ref() } } impl<'a> std::fmt::Debug for Value<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for line in hexdump::hexdump_iter(self.as_ref()) { f.write_str(&line)?; f.write_str("\n")?; } Ok(()) } } impl<'a> IValue<'a> for Vec { fn take_maybe(&mut self) -> Vec { std::mem::take(self) } } impl<'a> From> for Vec { fn from(v: Value<'a>) -> Vec { v.into_vec() } } impl<'a> From> for Value<'a> { fn from(v: Vec) -> Value<'a> { Value(Box::new(v)) } } impl<'a> From<&'a [u8]> for Value<'a> { fn from(v: &'a [u8]) -> Value<'a> { Value(Box::new(v)) } } impl<'a> IValue<'a> for &'a [u8] { fn take_maybe(&mut self) -> Vec { self.to_vec() } } // ---- #[derive(Debug, Error)] #[error(display = "{}", _0)] pub struct Error(pub Cow<'static, str>); pub type Result = std::result::Result; #[derive(Debug)] pub enum TxError { Abort(E), Db(Error), } pub type TxResult = std::result::Result>; impl From for TxError { fn from(e: Error) -> TxError { TxError::Db(e) } } // ---- impl Db { pub fn open_tree>(&self, name: S) -> Result { let tree_id = self.0.open_tree(name.as_ref())?; Ok(Tree(self.0.clone(), tree_id)) } pub fn list_trees(&self) -> Result> { self.0.list_trees() } pub fn transaction(&self, fun: F) -> TxResult where F: Fn(Transaction<'_>) -> TxResult, { let f = TxFn { function: fun, result: Cell::new(None), }; let tx_res = self.0.transaction(&f); let ret = f .result .into_inner() .expect("Transaction did not store result"); match tx_res { Ok(()) => { assert!(matches!(ret, Ok(_))); ret } Err(TxError::Abort(())) => { assert!(matches!(ret, Err(TxError::Abort(_)))); ret } Err(TxError::Db(e2)) => match ret { // Ok was stored -> the error occured when finalizing // transaction Ok(_) => Err(TxError::Db(e2)), // An error was already stored: that's the one we want to // return Err(TxError::Db(e)) => Err(TxError::Db(e)), _ => unreachable!(), }, } } pub fn import(&self, other: &Db) -> Result<()> { let existing_trees = self.list_trees()?; if !existing_trees.is_empty() { return Err(Error( format!( "destination database already contains data: {:?}", existing_trees ) .into(), )); } let tree_names = other.list_trees()?; for name in tree_names { let tree = self.open_tree(&name)?; if tree.len()? > 0 { return Err(Error(format!("tree {} already contains data", name).into())); } let ex_tree = other.open_tree(&name)?; let tx_res = self.transaction(|mut tx| { let mut i = 0; for item in ex_tree.iter()? { let (k, v) = item?; tx.insert(&tree, k, v)?; i += 1; if i % 1000 == 0 { println!("{}: imported {}", name, i); } } Ok::<_, TxError<()>>(i) }); let total = match tx_res { Err(TxError::Db(e)) => return Err(e), Err(TxError::Abort(_)) => unreachable!(), Ok(x) => x, }; println!("{}: finished importing, {} items", name, total); } Ok(()) } } #[allow(clippy::len_without_is_empty)] impl Tree { pub fn db(&self) -> Db { Db(self.0.clone()) } pub fn get>(&self, key: T) -> Result>> { self.0.get(self.1, key.as_ref()) } pub fn len(&self) -> Result { self.0.len(self.1) } pub fn first(&self) -> Result, Value<'_>)>> { self.iter()?.next().transpose() } pub fn get_gt>(&self, from: T) -> Result, Value<'_>)>> { self.range((Bound::Excluded(from), Bound::Unbounded))? .next() .transpose() } pub fn insert, U: AsRef<[u8]>>(&self, key: T, value: U) -> Result<()> { self.0.insert(self.1, key.as_ref(), value.as_ref()) } pub fn remove>(&self, key: T) -> Result { self.0.remove(self.1, key.as_ref()) } pub fn iter(&self) -> Result> { self.0.iter(self.1) } pub fn iter_rev(&self) -> Result> { self.0.iter_rev(self.1) } pub fn range(&self, range: R) -> Result> where K: AsRef<[u8]>, R: RangeBounds, { let sb = range.start_bound(); let eb = range.end_bound(); self.0.range(self.1, get_bound(sb), get_bound(eb)) } pub fn range_rev(&self, range: R) -> Result> where K: AsRef<[u8]>, R: RangeBounds, { let sb = range.start_bound(); let eb = range.end_bound(); self.0.range_rev(self.1, get_bound(sb), get_bound(eb)) } } #[allow(clippy::len_without_is_empty)] impl<'a> Transaction<'a> { pub fn get>(&self, tree: &Tree, key: T) -> Result>> { self.0.get(tree.1, key.as_ref()) } pub fn len(&self, tree: &Tree) -> Result { self.0.len(tree.1) } pub fn insert, U: AsRef<[u8]>>( &mut self, tree: &Tree, key: T, value: U, ) -> Result<()> { self.0.insert(tree.1, key.as_ref(), value.as_ref()) } pub fn remove>(&mut self, tree: &Tree, key: T) -> Result { self.0.remove(tree.1, key.as_ref()) } pub fn iter(&self, tree: &Tree) -> Result> { self.0.iter(tree.1) } pub fn iter_rev(&self, tree: &Tree) -> Result> { self.0.iter_rev(tree.1) } pub fn range(&self, tree: &Tree, range: R) -> Result> where K: AsRef<[u8]>, R: RangeBounds, { let sb = range.start_bound(); let eb = range.end_bound(); self.0.range(tree.1, get_bound(sb), get_bound(eb)) } pub fn range_rev(&self, tree: &Tree, range: R) -> Result> where K: AsRef<[u8]>, R: RangeBounds, { let sb = range.start_bound(); let eb = range.end_bound(); self.0.range_rev(tree.1, get_bound(sb), get_bound(eb)) } // ---- pub fn abort(self, e: E) -> TxResult { Err(TxError::Abort(e)) } pub fn commit(self, r: R) -> TxResult { Ok(r) } } // ---- Internal interfaces pub(crate) trait IDb: Send + Sync { fn open_tree(&self, name: &str) -> Result; fn list_trees(&self) -> Result>; fn get(&self, tree: usize, key: &[u8]) -> Result>>; fn len(&self, tree: usize) -> Result; fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()>; fn remove(&self, tree: usize, key: &[u8]) -> Result; fn iter(&self, tree: usize) -> Result>; fn iter_rev(&self, tree: usize) -> Result>; fn range<'r>( &self, tree: usize, low: Bound<&'r [u8]>, high: Bound<&'r [u8]>, ) -> Result>; fn range_rev<'r>( &self, tree: usize, low: Bound<&'r [u8]>, high: Bound<&'r [u8]>, ) -> Result>; fn transaction(&self, f: &dyn ITxFn) -> TxResult<(), ()>; } pub(crate) trait ITx { fn get(&self, tree: usize, key: &[u8]) -> Result>>; fn len(&self, tree: usize) -> Result; fn insert(&mut self, tree: usize, key: &[u8], value: &[u8]) -> Result<()>; fn remove(&mut self, tree: usize, key: &[u8]) -> Result; fn iter(&self, tree: usize) -> Result>; fn iter_rev(&self, tree: usize) -> Result>; fn range<'r>( &self, tree: usize, low: Bound<&'r [u8]>, high: Bound<&'r [u8]>, ) -> Result>; fn range_rev<'r>( &self, tree: usize, low: Bound<&'r [u8]>, high: Bound<&'r [u8]>, ) -> Result>; } pub(crate) trait ITxFn { fn try_on(&self, tx: &mut dyn ITx) -> TxFnResult; } pub(crate) enum TxFnResult { Ok, Abort, DbErr, } struct TxFn where F: Fn(Transaction<'_>) -> TxResult, { function: F, result: Cell>>, } impl ITxFn for TxFn where F: Fn(Transaction<'_>) -> TxResult, { fn try_on(&self, tx: &mut dyn ITx) -> TxFnResult { let res = (self.function)(Transaction(tx)); let res2 = match &res { Ok(_) => TxFnResult::Ok, Err(TxError::Abort(_)) => TxFnResult::Abort, Err(TxError::Db(_)) => TxFnResult::DbErr, }; self.result.set(Some(res)); res2 } } // ---- fn get_bound>(b: Bound<&K>) -> Bound<&[u8]> { match b { Bound::Included(v) => Bound::Included(v.as_ref()), Bound::Excluded(v) => Bound::Excluded(v.as_ref()), Bound::Unbounded => Bound::Unbounded, } }