Add TypedTree

This commit is contained in:
Arthur Carcano
2026-06-10 17:09:33 +02:00
committed by Alex
parent 22e003f0d0
commit eb91f463f5
7 changed files with 509 additions and 203 deletions
+97 -57
View File
@@ -9,6 +9,7 @@ pub mod lmdb_adapter;
pub mod sqlite_adapter;
pub mod open;
pub mod typed;
#[cfg(test)]
pub mod test;
@@ -23,6 +24,7 @@ use std::sync::Arc;
use thiserror::Error;
pub use open::*;
pub use typed::{DbBytes, DbOrdKey, TypedIter, TypedTree, TypedTxIter};
pub(crate) type OnCommit = Vec<Box<dyn FnOnce()>>;
@@ -38,21 +40,34 @@ pub struct Transaction<'a> {
pub struct Tree(Arc<dyn IDb>, usize);
pub type Value = Vec<u8>;
pub type ValueIter<'a> = Box<dyn std::iter::Iterator<Item = Result<(Value, Value)>> + 'a>;
pub type TxValueIter<'a> = Box<dyn std::iter::Iterator<Item = TxOpResult<(Value, Value)>> + 'a>;
pub type ValueIter<'a> = Box<dyn std::iter::Iterator<Item = DbResult<(Value, Value)>> + 'a>;
pub type TxValueIter<'a> = Box<dyn std::iter::Iterator<Item = DbResult<(Value, Value)>> + 'a>;
// ----
#[derive(Debug, Error)]
#[error("{0}")]
pub struct Error(pub Cow<'static, str>);
#[error("database error: {0}")]
pub struct DbError(pub Cow<'static, str>);
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Error {
Error(format!("IO: {}", e).into())
#[derive(Debug, Error)]
#[error("decode error: {0}")]
pub struct DecodeError(pub Cow<'static, str>);
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
Db(#[from] DbError),
#[error(transparent)]
Decode(#[from] DecodeError),
}
impl From<std::io::Error> for DbError {
fn from(e: std::io::Error) -> DbError {
DbError(format!("IO: {}", e).into())
}
}
pub type DbResult<T> = std::result::Result<T, DbError>;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error)]
@@ -60,6 +75,18 @@ pub type Result<T> = std::result::Result<T, Error>;
pub struct TxOpError(pub(crate) Error);
pub type TxOpResult<T> = std::result::Result<T, TxOpError>;
impl From<DbError> for TxOpError {
fn from(e: DbError) -> TxOpError {
TxOpError(e.into())
}
}
impl From<DecodeError> for TxOpError {
fn from(e: DecodeError) -> TxOpError {
TxOpError(e.into())
}
}
#[derive(Debug)]
pub enum TxError<E> {
Abort(E),
@@ -73,6 +100,12 @@ impl<E> From<TxOpError> for TxError<E> {
}
}
impl<E> From<DbError> for TxError<E> {
fn from(e: DbError) -> TxError<E> {
TxError::Db(e.into())
}
}
pub fn unabort<R, E>(res: TxResult<R, E>) -> TxOpResult<std::result::Result<R, E>> {
match res {
Ok(v) => Ok(Ok(v)),
@@ -88,12 +121,12 @@ impl Db {
self.0.engine()
}
pub fn open_tree<S: AsRef<str>>(&self, name: S) -> Result<Tree> {
pub fn open_tree<S: AsRef<str>>(&self, name: S) -> DbResult<Tree> {
let tree_id = self.0.open_tree(name.as_ref())?;
Ok(Tree(self.0.clone(), tree_id))
}
pub fn list_trees(&self) -> Result<Vec<String>> {
pub fn list_trees(&self) -> DbResult<Vec<String>> {
self.0.list_trees()
}
@@ -147,27 +180,28 @@ impl Db {
}
}
pub fn snapshot(&self, path: &Path) -> Result<()> {
pub fn snapshot(&self, path: &Path) -> DbResult<()> {
self.0.snapshot(path)
}
pub fn import(&self, other: &Db) -> Result<()> {
let existing_trees = self.list_trees()?;
if !existing_trees.is_empty() {
return Err(Error(
return Err(DbError(
format!(
"destination database already contains data: {:?}",
existing_trees
)
.into(),
));
)
.into());
}
let tree_names = other.list_trees()?;
for name in tree_names {
let tree = self.open_tree(&name)?;
if !tree.is_empty()? {
return Err(Error(format!("tree {} already contains data", name).into()));
return Err(DbError(format!("tree {} already contains data", name).into()).into());
}
let ex_tree = other.open_tree(&name)?;
@@ -186,7 +220,7 @@ impl Db {
});
let total = match tx_res {
Err(TxError::Db(e)) => return Err(e),
Err(TxError::Abort(e)) => return Err(e),
Err(TxError::Abort(e)) => return Err(e.into()),
Ok(x) => x,
};
@@ -204,24 +238,24 @@ impl Tree {
}
#[inline]
pub fn get<T: AsRef<[u8]>>(&self, key: T) -> Result<Option<Value>> {
pub fn get<T: AsRef<[u8]>>(&self, key: T) -> DbResult<Option<Value>> {
self.0.get(self.1, key.as_ref())
}
#[inline]
pub fn approximate_len(&self) -> Result<usize> {
pub fn approximate_len(&self) -> DbResult<usize> {
self.0.approximate_len(self.1)
}
#[inline]
pub fn is_empty(&self) -> Result<bool> {
pub fn is_empty(&self) -> DbResult<bool> {
self.0.is_empty(self.1)
}
#[inline]
pub fn first(&self) -> Result<Option<(Value, Value)>> {
pub fn first(&self) -> DbResult<Option<(Value, Value)>> {
self.iter()?.next().transpose()
}
#[inline]
pub fn get_gt<T: AsRef<[u8]>>(&self, from: T) -> Result<Option<(Value, Value)>> {
pub fn get_gt<T: AsRef<[u8]>>(&self, from: T) -> DbResult<Option<(Value, Value)>> {
if from.as_ref().is_empty() {
self.iter()?.next().transpose()
} else {
@@ -233,31 +267,31 @@ impl Tree {
/// Returns the old value if there was one
#[inline]
pub fn insert<T: AsRef<[u8]>, U: AsRef<[u8]>>(&self, key: T, value: U) -> Result<()> {
pub fn insert<T: AsRef<[u8]>, U: AsRef<[u8]>>(&self, key: T, value: U) -> DbResult<()> {
self.0.insert(self.1, key.as_ref(), value.as_ref())
}
/// Returns the old value if there was one
#[inline]
pub fn remove<T: AsRef<[u8]>>(&self, key: T) -> Result<()> {
pub fn remove<T: AsRef<[u8]>>(&self, key: T) -> DbResult<()> {
self.0.remove(self.1, key.as_ref())
}
/// Clears all values from the tree
#[inline]
pub fn clear(&self) -> Result<()> {
pub fn clear(&self) -> DbResult<()> {
self.0.clear(self.1)
}
#[inline]
pub fn iter(&self) -> Result<ValueIter<'_>> {
pub fn iter(&self) -> DbResult<ValueIter<'_>> {
self.0.iter(self.1)
}
#[inline]
pub fn iter_rev(&self) -> Result<ValueIter<'_>> {
pub fn iter_rev(&self) -> DbResult<ValueIter<'_>> {
self.0.iter_rev(self.1)
}
#[inline]
pub fn range<K, R>(&self, range: R) -> Result<ValueIter<'_>>
pub fn range<K, R>(&self, range: R) -> DbResult<ValueIter<'_>>
where
K: AsRef<[u8]>,
R: RangeBounds<K>,
@@ -267,7 +301,7 @@ impl Tree {
self.0.range(self.1, get_bound(sb), get_bound(eb))
}
#[inline]
pub fn range_rev<K, R>(&self, range: R) -> Result<ValueIter<'_>>
pub fn range_rev<K, R>(&self, range: R) -> DbResult<ValueIter<'_>>
where
K: AsRef<[u8]>,
R: RangeBounds<K>,
@@ -282,11 +316,11 @@ impl Tree {
impl<'a> Transaction<'a> {
#[inline]
pub fn get<T: AsRef<[u8]>>(&self, tree: &Tree, key: T) -> TxOpResult<Option<Value>> {
self.tx.get(tree.1, key.as_ref())
self.tx.get(tree.1, key.as_ref()).map_err(Into::into)
}
#[inline]
pub fn len(&self, tree: &Tree) -> TxOpResult<usize> {
self.tx.len(tree.1)
self.tx.len(tree.1).map_err(Into::into)
}
/// Returns the old value if there was one
@@ -297,26 +331,28 @@ impl<'a> Transaction<'a> {
key: T,
value: U,
) -> TxOpResult<()> {
self.tx.insert(tree.1, key.as_ref(), value.as_ref())
self.tx
.insert(tree.1, key.as_ref(), value.as_ref())
.map_err(Into::into)
}
/// Returns the old value if there was one
#[inline]
pub fn remove<T: AsRef<[u8]>>(&mut self, tree: &Tree, key: T) -> TxOpResult<()> {
self.tx.remove(tree.1, key.as_ref())
self.tx.remove(tree.1, key.as_ref()).map_err(Into::into)
}
/// Clears all values in a tree
#[inline]
pub fn clear(&mut self, tree: &Tree) -> TxOpResult<()> {
self.tx.clear(tree.1)
self.tx.clear(tree.1).map_err(Into::into)
}
#[inline]
pub fn iter(&self, tree: &Tree) -> TxOpResult<TxValueIter<'_>> {
self.tx.iter(tree.1)
self.tx.iter(tree.1).map_err(Into::into)
}
#[inline]
pub fn iter_rev(&self, tree: &Tree) -> TxOpResult<TxValueIter<'_>> {
self.tx.iter_rev(tree.1)
self.tx.iter_rev(tree.1).map_err(Into::into)
}
#[inline]
@@ -327,7 +363,9 @@ impl<'a> Transaction<'a> {
{
let sb = range.start_bound();
let eb = range.end_bound();
self.tx.range(tree.1, get_bound(sb), get_bound(eb))
self.tx
.range(tree.1, get_bound(sb), get_bound(eb))
.map_err(Into::into)
}
#[inline]
pub fn range_rev<K, R>(&self, tree: &Tree, range: R) -> TxOpResult<TxValueIter<'_>>
@@ -337,7 +375,9 @@ impl<'a> Transaction<'a> {
{
let sb = range.start_bound();
let eb = range.end_bound();
self.tx.range_rev(tree.1, get_bound(sb), get_bound(eb))
self.tx
.range_rev(tree.1, get_bound(sb), get_bound(eb))
.map_err(Into::into)
}
#[inline]
@@ -350,60 +390,60 @@ impl<'a> Transaction<'a> {
pub(crate) trait IDb: Send + Sync {
fn engine(&self) -> String;
fn open_tree(&self, name: &str) -> Result<usize>;
fn list_trees(&self) -> Result<Vec<String>>;
fn snapshot(&self, path: &Path) -> Result<()>;
fn open_tree(&self, name: &str) -> DbResult<usize>;
fn list_trees(&self) -> DbResult<Vec<String>>;
fn snapshot(&self, path: &Path) -> DbResult<()>;
fn get(&self, tree: usize, key: &[u8]) -> Result<Option<Value>>;
fn approximate_len(&self, tree: usize) -> Result<usize>;
fn is_empty(&self, tree: usize) -> Result<bool>;
fn get(&self, tree: usize, key: &[u8]) -> DbResult<Option<Value>>;
fn approximate_len(&self, tree: usize) -> DbResult<usize>;
fn is_empty(&self, tree: usize) -> DbResult<bool>;
fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()>;
fn remove(&self, tree: usize, key: &[u8]) -> Result<()>;
fn clear(&self, tree: usize) -> Result<()>;
fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> DbResult<()>;
fn remove(&self, tree: usize, key: &[u8]) -> DbResult<()>;
fn clear(&self, tree: usize) -> DbResult<()>;
fn iter(&self, tree: usize) -> Result<ValueIter<'_>>;
fn iter_rev(&self, tree: usize) -> Result<ValueIter<'_>>;
fn iter(&self, tree: usize) -> DbResult<ValueIter<'_>>;
fn iter_rev(&self, tree: usize) -> DbResult<ValueIter<'_>>;
fn range<'r>(
&self,
tree: usize,
low: Bound<&'r [u8]>,
high: Bound<&'r [u8]>,
) -> Result<ValueIter<'_>>;
) -> DbResult<ValueIter<'_>>;
fn range_rev<'r>(
&self,
tree: usize,
low: Bound<&'r [u8]>,
high: Bound<&'r [u8]>,
) -> Result<ValueIter<'_>>;
) -> DbResult<ValueIter<'_>>;
fn transaction(&self, f: &dyn ITxFn) -> TxResult<OnCommit, ()>;
}
pub(crate) trait ITx {
fn get(&self, tree: usize, key: &[u8]) -> TxOpResult<Option<Value>>;
fn len(&self, tree: usize) -> TxOpResult<usize>;
fn get(&self, tree: usize, key: &[u8]) -> DbResult<Option<Value>>;
fn len(&self, tree: usize) -> DbResult<usize>;
fn insert(&mut self, tree: usize, key: &[u8], value: &[u8]) -> TxOpResult<()>;
fn remove(&mut self, tree: usize, key: &[u8]) -> TxOpResult<()>;
fn clear(&mut self, tree: usize) -> TxOpResult<()>;
fn insert(&mut self, tree: usize, key: &[u8], value: &[u8]) -> DbResult<()>;
fn remove(&mut self, tree: usize, key: &[u8]) -> DbResult<()>;
fn clear(&mut self, tree: usize) -> DbResult<()>;
fn iter(&self, tree: usize) -> TxOpResult<TxValueIter<'_>>;
fn iter_rev(&self, tree: usize) -> TxOpResult<TxValueIter<'_>>;
fn iter(&self, tree: usize) -> DbResult<TxValueIter<'_>>;
fn iter_rev(&self, tree: usize) -> DbResult<TxValueIter<'_>>;
fn range<'r>(
&self,
tree: usize,
low: Bound<&'r [u8]>,
high: Bound<&'r [u8]>,
) -> TxOpResult<TxValueIter<'_>>;
) -> DbResult<TxValueIter<'_>>;
fn range_rev<'r>(
&self,
tree: usize,
low: Bound<&'r [u8]>,
high: Bound<&'r [u8]>,
) -> TxOpResult<TxValueIter<'_>>;
) -> DbResult<TxValueIter<'_>>;
}
pub(crate) trait ITxFn {