pub mod err {
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("I/O error: `{0}`")]
Io(#[from] std::io::Error),
#[error("Database error: `{0}`")]
Sled(#[from] sled::Error),
#[cfg(feature = "bincode")]
#[error("De/serialization error: `{0}`")]
Bincode(#[from] bincode::Error),
#[cfg(feature = "serde_cbor")]
#[error("De/serialization error: `{0}`")]
CBOR(#[from] serde_cbor::Error),
#[error("Error: `{0}`")]
Custom(Box<str>),
}
impl From<sled::transaction::TransactionError<Error>> for Error {
fn from(t: sled::transaction::TransactionError<Error>) -> Self {
match t {
sled::transaction::TransactionError::Abort(t) => t,
sled::transaction::TransactionError::Storage(t) => Error::Sled(t),
}
}
}
impl From<Error> for sled::transaction::ConflictableTransactionError<Error> {
fn from(t: Error) -> Self {
sled::transaction::ConflictableTransactionError::Abort(t)
}
}
pub fn custom<T: std::fmt::Display>(t: T) -> Error {
Error::Custom(t.to_string().into_boxed_str())
}
pub type Result<T> = std::result::Result<T, Error>;
}
mod utils {
use std::convert::TryInto;
use sled::transaction::TransactionalTree;
#[cfg(feature = "bincode")]
pub fn serialize<T: ?Sized + serde::Serialize>(value: &T) -> crate::err::Result<Vec<u8>> {
Ok(bincode::serialize(value)?)
}
#[cfg(feature = "bincode")]
pub fn deserialize<T: serde::de::DeserializeOwned>(bytes: &[u8]) -> crate::err::Result<T> {
Ok(bincode::deserialize(bytes)?)
}
pub fn u64_to_bytes(value: u64) -> [u8; 8] {
u64::to_be_bytes(value)
}
pub fn bytes_to_u64(value: &[u8]) -> crate::err::Result<u64> {
Ok(u64::from_be_bytes(value.try_into().map_err(crate::err::custom)?))
}
}
pub mod query;
mod ext;
use sled::transaction::Transactional;
use sled::transaction::ConflictableTransactionError;
use std::marker::PhantomData;
use std::collections::BTreeSet;
pub struct StoreBuilder<T> {
db: Option<sled::Db>,
tree_name: Option<Vec<u8>>,
meta_name: Option<Vec<u8>>,
marker: PhantomData<fn(T)>,
}
impl<T> Default for StoreBuilder<T> {
fn default() -> Self {
Self {
db: None,
tree_name: None,
meta_name: None,
marker: PhantomData,
}
}
}
impl<T> StoreBuilder<T> {
pub fn with_db(mut self, db: sled::Db) -> Self {
self.db = Some(db);
self
}
pub fn with_tree_name<S: Into<Vec<u8>>>(mut self, tree_name: S) -> Self {
self.tree_name = Some(tree_name.into());
self
}
pub fn with_meta_name<S: Into<Vec<u8>>>(mut self, meta_name: S) -> Self {
self.meta_name = Some(meta_name.into());
self
}
pub fn finish(self) -> err::Result<Store<T>> {
let Self { db, tree_name, meta_name, marker } = self;
let db = match db {
Some(db) => db,
None => { return Err(err::custom("No `db` provided")); }
};
let tree_name = match tree_name {
Some(tree_name) => tree_name,
None => { return Err(err::custom("No `tree_name` specified")); }
};
let meta_name = meta_name.unwrap_or_else(|| {
tree_name.iter().chain(b"\0meta").copied().collect()
});
Ok(Store {
tree: db.open_tree(&tree_name)?,
meta: db.open_tree(&meta_name)?,
db,
marker
})
}
}
pub struct Store<T> {
db: sled::Db,
tree: sled::Tree,
meta: sled::Tree,
marker: PhantomData<fn(T)>,
}
pub struct TransactionalStore<'a, T> {
tree: &'a sled::transaction::TransactionalTree,
meta: ext::TransactionalMeta<'a>,
marker: PhantomData<fn(T)>,
}
impl<'a, T: query::Queryable + serde::Serialize + serde::de::DeserializeOwned> TransactionalStore<'a, T> {
pub fn create(&self, id: u64, inner: &T) -> Result<u64, ConflictableTransactionError<err::Error>> {
let id_bytes = utils::u64_to_bytes(id);
let serialized_inner = utils::serialize(inner)?;
self.tree.insert(&id_bytes, serialized_inner)?;
let new_terms =
inner.query_terms().into_iter().map(|t| t.flatten()).collect::<BTreeSet<_>>();
self.meta.add(Some(new_terms), id, &id_bytes)?;
Ok(id)
}
pub fn create_multi(&self, ids: Vec<u64>, inners: &[T]) -> Result<Vec<u64>, ConflictableTransactionError<err::Error>> {
for (id, inner) in ids.clone().into_iter().zip(inners) {
let id_bytes = utils::u64_to_bytes(id);
let serialized_inner = utils::serialize(inner)?;
self.tree.insert(&id_bytes, serialized_inner)?;
let new_terms =
inner.query_terms().into_iter().map(|t| t.flatten()).collect::<BTreeSet<_>>();
self.meta.add(Some(new_terms), id, &id_bytes)?;
}
Ok(ids)
}
pub fn update(&self, object: &Object<T>) -> Result<(), ConflictableTransactionError<err::Error>> {
self.update_multi(std::slice::from_ref(object))
}
pub fn update_multi(&self, objects: &[Object<T>]) -> Result<(), ConflictableTransactionError<err::Error>> {
for Object { id, inner } in objects {
let id_bytes = utils::u64_to_bytes(*id);
let serialized_inner = utils::serialize(inner)?;
self.tree.insert(&id_bytes, serialized_inner)?;
let new_terms =
inner.query_terms().into_iter().map(|t| t.flatten()).collect::<BTreeSet<_>>();
self.meta.add(Some(new_terms), *id, &id_bytes)?;
}
Ok(())
}
pub fn delete(&self, id: u64) -> Result<(), ConflictableTransactionError<err::Error>> {
self.delete_multi(&[id])
}
pub fn delete_multi(&self, ids: &[u64]) -> Result<(), ConflictableTransactionError<err::Error>> {
for id in ids {
let id_bytes = utils::u64_to_bytes(*id);
self.meta.add(None, *id, &id_bytes)?;
self.tree.remove(&id_bytes)?;
}
Ok(())
}
pub fn find(&self, id: u64) -> Result<Option<Object<T>>, ConflictableTransactionError<err::Error>> {
Ok(self
.tree
.get(utils::u64_to_bytes(id))?
.map(|bytes| utils::deserialize(&bytes))
.transpose()?
.map(|inner| Object { id, inner }))
}
pub fn filter<Q: query::TransactionalQuery<sled::transaction::TransactionalTree>>(&self, query: Q) -> Result<query::TransactionalResults<T>, ConflictableTransactionError<err::Error>> {
let matching_ids = query.matching_ids(&self.meta.0)?;
Ok(query::TransactionalResults { matching_ids, store: self })
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Object<T> {
pub id: u64,
pub inner: T,
}
impl<T> std::ops::Deref for Object<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> std::ops::DerefMut for Object<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T: query::Queryable + serde::Serialize + serde::de::DeserializeOwned> Store<T> {
pub fn transaction<A, F>(&self, f: F) -> sled::transaction::TransactionResult<A, err::Error>
where F: Fn(&TransactionalStore<T>) -> sled::transaction::ConflictableTransactionResult<A, err::Error>,
{
[&self.tree, &self.meta].transaction(|trees| {
let tree = &trees[0];
let meta = &trees[1];
let transactional_store = TransactionalStore { tree, meta: ext::TransactionalMeta(meta), marker: PhantomData };
f(&transactional_store)
})
}
pub fn new(&self, inner: T) -> err::Result<Object<T>> {
let id = self.db.generate_id()?;
Ok(Object { id, inner })
}
pub fn create(&self, inner: &T) -> err::Result<u64> {
let id = self.db.generate_id()?;
Ok(self.transaction(|store| store.create(id, inner))?)
}
pub fn create_multi(&self, inners: &[T]) -> err::Result<Vec<u64>> {
let ids = (0..inners.len())
.map(|_| self.db.generate_id() )
.collect::<Result<Vec<_>, _>>()?;
Ok(self.transaction(|store| store.create_multi(ids.clone(), inners))?)
}
pub fn update(&self, object: &Object<T>) -> err::Result<()> {
self.update_multi(std::slice::from_ref(object))
}
pub fn update_multi(&self, objects: &[Object<T>]) -> err::Result<()> {
Ok(self.transaction(|store| store.update_multi(objects))?)
}
pub fn delete(&self, id: u64) -> err::Result<()> {
self.delete_multi(&[id])
}
pub fn delete_multi(&self, ids: &[u64]) -> err::Result<()> {
Ok(self.transaction(|store| store.delete_multi(ids))?)
}
pub fn all(&self) -> err::Result<Vec<Object<T>>> {
Ok(self
.tree
.iter()
.flatten()
.map(|(k, v)| {
Ok(Object {
id: utils::bytes_to_u64(k.as_ref())?,
inner: utils::deserialize(&v)?,
})
})
.collect::<err::Result<Vec<_>>>()?)
}
pub fn find(&self, id: u64) -> err::Result<Option<Object<T>>> {
Ok(self
.tree
.get(utils::u64_to_bytes(id))?
.map(|bytes| utils::deserialize(&bytes))
.transpose()?
.map(|inner| Object { id, inner }))
}
pub fn filter<Q: query::Query>(&self, query: Q) -> err::Result<query::Results<T>> {
let matching_ids = query.matching_ids(&self.meta)?;
Ok(query::Results { matching_ids, store: self })
}
pub fn delete_all(&self) -> err::Result<()> {
let tree_keys = self.tree.iter().keys().collect::<Vec<_>>();
let meta_keys = self.meta.iter().keys().collect::<Vec<_>>();
[&self.tree, &self.meta].transaction(|trees| {
let tree = &trees[0];
let meta = &trees[1];
for key in tree_keys.clone() {
let key = key?;
tree.remove(key)?;
}
for key in meta_keys.clone() {
let key = key?;
meta.remove(key)?;
}
Ok(())
})?;
Ok(())
}
}