use std::{borrow::Borrow, fmt::Debug, ops::Deref};
use armour_derive::armour_metrics;
use fjall::{Keyspace, KeyspaceCreateOptions, Slice};
use super::{ByteValue, RawTree, builder::TreeBuilder, db::Db, read::ReadTree};
use crate::{Cid, DbError, DbResult, Record, indexes::IndexUpdateCollection};
use arrayvec::ArrayVec;
pub type IndexesMap<Item> = ArrayVec<Box<dyn IndexUpdateCollection<Item>>, 8>;
#[derive(derive_more::Debug)]
pub struct TypedTree<Item: Record> {
pub inner: ReadTree<Item>,
}
impl<Item: Record> Clone for TypedTree<Item> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<Item: Record> Deref for TypedTree<Item> {
type Target = ReadTree<Item>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<Item> TypedTree<Item>
where
Item: Record<Value = Slice> + std::fmt::Debug,
{
#[doc(alias = "new")]
#[doc(alias = "create")]
pub fn open(db: &Db, options: Option<KeyspaceCreateOptions>) -> Self {
TreeBuilder::<Item>::new(db, options).build()
}
pub fn with_name(db: &Db, name: &str, options: Option<KeyspaceCreateOptions>) -> Self {
TreeBuilder::<Item>::with_name(db, name, options).build()
}
pub fn builder(db: &Db, options: Option<KeyspaceCreateOptions>) -> TreeBuilder<Item> {
TreeBuilder::<Item>::new(db, options)
}
#[doc(alias = "inner")]
pub fn raw(&self) -> &RawTree {
&self.inner.raw
}
pub(crate) fn tree(&self) -> &Keyspace {
&self.inner.raw.tree
}
#[inline]
pub(super) fn invalidate_hash(&self, id: &Item::SelfId) {
let group = id.group_id();
self.inner.raw.inner.invalidate_hash(group);
}
}
impl<Item: Record> From<TypedTree<Item>> for RawTree {
fn from(value: TypedTree<Item>) -> Self {
value.inner.raw
}
}
impl<Item> TypedTree<Item>
where
Item: Record<Value = Slice> + Debug,
{
#[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
#[armour_metrics(prefix = "armour_logdb_tree", name = self.raw().static_name())]
pub fn upsert(&self, id: impl Borrow<Item::SelfId> + Debug, item: &Item) -> DbResult<()> {
let bytes = Item::ser(item);
let key = Cid::encode(id.borrow());
let key_bytes = ByteValue::from(key.as_ref());
self.tree().insert(key_bytes, bytes)?;
self.invalidate_hash(id.borrow());
Ok(())
}
#[doc(alias = "create")]
#[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
#[armour_metrics(prefix = "armour_logdb_tree", name = self.raw().static_name())]
pub fn insert(&self, id: impl Borrow<Item::SelfId> + Debug, item: &Item) -> DbResult<()> {
let bytes = Item::ser(item);
let key = Cid::encode(id.borrow());
let key_bytes = ByteValue::from(key.as_ref());
if self.tree().get(key.as_ref())?.is_some() {
return Err(DbError::Client("already exists"));
}
self.tree().insert(key_bytes, bytes)?;
self.invalidate_hash(id.borrow());
Ok(())
}
#[doc(alias = "replace")]
#[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
#[armour_metrics(prefix = "armour_logdb_tree", name = self.raw().static_name())]
pub fn update(
&self,
id: impl Borrow<Item::SelfId> + Debug,
item: &Item,
) -> DbResult<Option<Item>> {
let bytes = Item::ser(item);
let key = Cid::encode(id.borrow());
let key_bytes = ByteValue::from(key.as_ref());
match self.tree().get(key.as_ref())? {
Some(val) => {
let old_item = Item::deser(&val);
self.tree().insert(key_bytes, bytes)?;
self.invalidate_hash(id.borrow());
Ok(Some(old_item))
}
None => Ok(None),
}
}
#[doc(alias = "delete")]
#[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
#[armour_metrics(prefix = "armour_logdb_tree", name = self.raw().static_name())]
pub fn remove(&self, id: impl Borrow<Item::SelfId> + Debug) -> DbResult<()> {
let key = Cid::encode(id.borrow());
let key_bytes = ByteValue::from(key.as_ref());
self.tree().remove(key_bytes).map_err(DbError::from)?;
self.invalidate_hash(id.borrow());
Ok(())
}
#[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
#[armour_metrics(prefix = "armour_logdb_tree", name = self.raw().static_name())]
pub fn take(&self, id: impl Borrow<Item::SelfId> + Debug) -> DbResult<Option<Item>> {
let key = Cid::encode(id.borrow());
let key_bytes = ByteValue::from(key.as_ref());
match self.tree().get(key.as_ref())? {
Some(val) => {
let item = Item::deser(&val);
self.tree().remove(key_bytes).map_err(DbError::from)?;
self.invalidate_hash(id.borrow());
Ok(Some(item))
}
None => Ok(None),
}
}
#[doc(alias = "delete")]
#[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
#[armour_metrics(prefix = "armour_logdb_tree", name = self.raw().static_name())]
pub fn soft_remove(&self, id: impl Borrow<Item::SelfId> + Debug) -> DbResult<Option<Item>> {
let key = Cid::encode(id.borrow());
let key_bytes = ByteValue::from(key.as_ref());
match self.tree().get(key.as_ref())? {
Some(val) => {
let item = Item::deser(&val);
self.raw().removed.insert(key_bytes.clone(), val)?;
self.tree().remove(key_bytes).map_err(DbError::from)?;
self.invalidate_hash(id.borrow());
Ok(Some(item))
}
None => Ok(None),
}
}
#[armour_metrics(prefix = "armour_logdb_tree", name = self.raw().static_name())]
pub fn apply_batch<K, V>(&self, iter: impl Iterator<Item = (K, Option<V>)>) -> DbResult<()>
where
K: AsRef<Item::SelfId>,
V: AsRef<Item>,
{
self.raw().apply_batch(iter.map(|(id, item)| {
let key_bytes = Cid::encode(id.as_ref());
let key_bytes = ByteValue::from(key_bytes.as_ref());
let val_bytes = item.map(|item| Item::ser(item.as_ref()));
(key_bytes, val_bytes)
}))
}
pub fn next_id(&self) -> DbResult<u64> {
self.raw().next_id()
}
}