diff options
Diffstat (limited to 'src/db/lib.rs')
-rw-r--r-- | src/db/lib.rs | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/src/db/lib.rs b/src/db/lib.rs new file mode 100644 index 00000000..0f23a9b4 --- /dev/null +++ b/src/db/lib.rs @@ -0,0 +1,174 @@ +pub mod sled_adapter; + +#[cfg(test)] +pub mod test; + +use std::borrow::Cow; +use std::sync::Arc; + +use arc_swap::ArcSwapOption; + +#[derive(Clone)] +pub struct Db(pub(crate) Arc<dyn IDb>); + +#[derive(Clone)] +pub struct Transaction<'a>(pub(crate) &'a dyn ITx<'a>); + +#[derive(Clone)] +pub struct Tree(pub(crate) Arc<dyn IDb>, pub(crate) usize); + +pub type Value<'a> = Cow<'a, [u8]>; + +// ---- + +#[derive(Debug)] +pub struct Error(Cow<'static, str>); + +pub type Result<T> = std::result::Result<T, Error>; + +#[derive(Debug)] +pub enum TxError<E> { + Abort(E), + Db(Error), +} +pub type TxResult<R, E> = std::result::Result<R, TxError<E>>; + +impl<E> From<Error> for TxError<E> { + fn from(e: Error) -> TxError<E> { + TxError::Db(e) + } +} + +// ---- + +impl Db { + pub fn tree<S: AsRef<str>>(&self, name: S) -> Result<Tree> { + let tree_id = self.0.tree(name.as_ref())?; + Ok(Tree(self.0.clone(), tree_id)) + } + + pub fn transaction<R, E, F>(&self, fun: F) -> TxResult<R, E> + where + F: Fn(Transaction<'_>) -> TxResult<R, E> + Send + Sync, + R: Send + Sync, + E: Send + Sync, + { + let f = TxFn { + function: fun, + result: ArcSwapOption::new(None), + }; + match self.0.transaction(&f) { + Err(TxError::Db(e)) => Err(TxError::Db(e)), + Err(TxError::Abort(())) => { + let r_arc = f + .result + .into_inner() + .expect("Transaction did not store result"); + let r = Arc::try_unwrap(r_arc).ok().expect("Many refs"); + assert!(matches!(r, Err(TxError::Abort(_)))); + r + } + Ok(()) => { + let r_arc = f + .result + .into_inner() + .expect("Transaction did not store result"); + let r = Arc::try_unwrap(r_arc).ok().expect("Many refs"); + assert!(matches!(r, Ok(_))); + r + } + } + } +} + +impl Tree { + pub fn get<'a, T: AsRef<[u8]>>(&'a self, key: T) -> Result<Option<Value<'a>>> { + self.0.get(self.1, key.as_ref()) + } + + pub fn put<T: AsRef<[u8]>, U: AsRef<[u8]>>(&self, key: T, value: U) -> Result<()> { + self.0.put(self.1, key.as_ref(), value.as_ref()) + } +} + +impl<'a> Transaction<'a> { + pub fn get<T: AsRef<[u8]>>(&self, tree: &Tree, key: T) -> Result<Option<Value<'a>>> { + self.0.get(tree.1, key.as_ref()) + } + + pub fn put<T: AsRef<[u8]>, U: AsRef<[u8]>>(&self, tree: &Tree, key: T, value: U) -> Result<()> { + self.0.put(tree.1, key.as_ref(), value.as_ref()) + } + + #[must_use] + pub fn abort<R, E>(self, e: E) -> TxResult<R, E> + where + R: Send + Sync, + E: Send + Sync, + { + Err(TxError::Abort(e)) + } + + #[must_use] + pub fn commit<R, E>(self, r: R) -> TxResult<R, E> + where + R: Send + Sync, + E: Send + Sync, + { + Ok(r) + } +} + +// ---- Internal interfaces + +pub(crate) trait IDb: Send + Sync { + fn tree(&self, name: &str) -> Result<usize>; + + fn get<'a>(&'a self, tree: usize, key: &[u8]) -> Result<Option<Value<'a>>>; + fn put(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()>; + + fn transaction(&self, f: &dyn ITxFn) -> TxResult<(), ()>; +} + +pub(crate) trait ITx<'a> { + fn get(&self, tree: usize, key: &[u8]) -> Result<Option<Value<'a>>>; + fn put(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()>; +} + +pub(crate) trait ITxFn: Send + Sync { + fn try_on<'a>(&'a self, tx: &'a dyn ITx<'a>) -> TxFnResult; +} + +enum TxFnResult { + Abort, + Ok, + Err, +} + +struct TxFn<F, R, E> +where + F: Fn(Transaction<'_>) -> TxResult<R, E> + Send + Sync, + R: Send + Sync, + E: Send + Sync, +{ + function: F, + result: ArcSwapOption<TxResult<R, E>>, +} + +impl<F, R, E> ITxFn for TxFn<F, R, E> +where + F: Fn(Transaction<'_>) -> TxResult<R, E> + Send + Sync, + R: Send + Sync, + E: Send + Sync, +{ + fn try_on<'a>(&'a self, tx: &'a dyn ITx<'a>) -> TxFnResult { + let res = (self.function)(Transaction(tx)); + let retval = match &res { + Ok(_) => TxFnResult::Ok, + Err(TxError::Abort(_)) => TxFnResult::Abort, + Err(TxError::Db(_)) => TxFnResult::Err, + }; + self.result.store(Some(Arc::new(res))); + retval + } +} |