use std::marker::PhantomData;
use std::str::FromStr;
use strum::IntoDiscriminant;
use crate::traits::convert::ToIVec;
use crate::traits::definition::NetabaseDefinitionTrait;
use crate::traits::model::NetabaseModelTrait;
use crate::{MaybeSend, MaybeSync, NetabaseModelTraitKey};
use super::types::SecondaryKeyOp;
pub struct SledTransactionalTree<D, M>
where
D: NetabaseDefinitionTrait,
M: NetabaseModelTrait<D>,
<D as IntoDiscriminant>::Discriminant: Clone
+ Copy
+ std::fmt::Debug
+ std::fmt::Display
+ PartialEq
+ Eq
+ std::hash::Hash
+ strum::IntoEnumIterator
+ MaybeSend
+ MaybeSync
+ 'static
+ FromStr,
<D as strum::IntoDiscriminant>::Discriminant: std::marker::Copy,
<D as strum::IntoDiscriminant>::Discriminant: std::fmt::Debug,
<D as strum::IntoDiscriminant>::Discriminant: std::hash::Hash,
<D as strum::IntoDiscriminant>::Discriminant: std::cmp::Eq,
<D as strum::IntoDiscriminant>::Discriminant: std::fmt::Display,
<D as strum::IntoDiscriminant>::Discriminant: FromStr,
<D as strum::IntoDiscriminant>::Discriminant: MaybeSync,
<D as strum::IntoDiscriminant>::Discriminant: MaybeSend,
<D as strum::IntoDiscriminant>::Discriminant: strum::IntoEnumIterator,
<D as strum::IntoDiscriminant>::Discriminant: std::convert::AsRef<str>,
{
pub(super) tree: sled::transaction::TransactionalTree,
pub(super) secondary_tree: sled::Tree,
pub(super) pending_secondary_keys:
Option<std::sync::Arc<std::sync::Mutex<Vec<SecondaryKeyOp>>>>,
pub(super) _phantom_d: PhantomData<D>,
pub(super) _phantom_m: PhantomData<M>,
}
impl<D, M> SledTransactionalTree<D, M>
where
D: NetabaseDefinitionTrait + TryFrom<M> + ToIVec + From<M>,
M: NetabaseModelTrait<D> + TryFrom<D> + Into<D>,
<D as IntoDiscriminant>::Discriminant: Clone
+ Copy
+ std::fmt::Debug
+ std::fmt::Display
+ PartialEq
+ Eq
+ std::hash::Hash
+ strum::IntoEnumIterator
+ MaybeSend
+ MaybeSync
+ 'static
+ FromStr,
<D as strum::IntoDiscriminant>::Discriminant: std::marker::Copy,
<D as strum::IntoDiscriminant>::Discriminant: std::fmt::Debug,
<D as strum::IntoDiscriminant>::Discriminant: std::hash::Hash,
<D as strum::IntoDiscriminant>::Discriminant: std::cmp::Eq,
<D as strum::IntoDiscriminant>::Discriminant: std::fmt::Display,
<D as strum::IntoDiscriminant>::Discriminant: FromStr,
<D as strum::IntoDiscriminant>::Discriminant: MaybeSync,
<D as strum::IntoDiscriminant>::Discriminant: MaybeSend,
<D as strum::IntoDiscriminant>::Discriminant: strum::IntoEnumIterator,
<D as strum::IntoDiscriminant>::Discriminant: std::convert::AsRef<str>,
{
pub fn put(&self, model: M) -> Result<(), Box<dyn std::error::Error>> {
let primary_key = model.primary_key();
let secondary_keys = model.secondary_keys();
let key_bytes = bincode::encode_to_vec(&primary_key, bincode::config::standard())?;
let definition: D = model.into();
let value_bytes = definition.to_ivec()?;
self.tree.insert(key_bytes, value_bytes.as_ref())?;
if !secondary_keys.is_empty() {
if let Some(pending) = &self.pending_secondary_keys {
let mut ops = pending.lock().unwrap();
for sec_key in secondary_keys.values() {
let composite_key = self.build_composite_key(sec_key, &primary_key)?;
ops.push(SecondaryKeyOp::Insert(composite_key));
}
} else {
for sec_key in secondary_keys.values() {
let composite_key = self.build_composite_key(sec_key, &primary_key)?;
self.secondary_tree.insert(composite_key, &[] as &[u8])?;
}
}
}
Ok(())
}
pub fn get(
&self,
key: <M::Keys as NetabaseModelTraitKey<D>>::PrimaryKey,
) -> Result<Option<M>, Box<dyn std::error::Error>> {
let key_bytes = bincode::encode_to_vec(&key, bincode::config::standard())?;
match self.tree.get(&key_bytes)? {
Some(ivec) => {
let definition = D::from_ivec(&ivec)?;
match M::try_from(definition) {
Ok(model) => Ok(Some(model)),
Err(_) => Ok(None),
}
}
None => Ok(None),
}
}
pub fn remove(
&self,
key: <M::Keys as NetabaseModelTraitKey<D>>::PrimaryKey,
) -> Result<Option<M>, Box<dyn std::error::Error>> {
let key_bytes = bincode::encode_to_vec(&key, bincode::config::standard())?;
match self.tree.remove(key_bytes.as_slice())? {
Some(ivec) => {
let definition = D::from_ivec(&ivec)?;
match M::try_from(definition) {
Ok(model) => {
let secondary_keys = model.secondary_keys();
if !secondary_keys.is_empty() {
if let Some(pending) = &self.pending_secondary_keys {
let mut ops = pending.lock().unwrap();
for sec_key in secondary_keys.values() {
let composite_key = self.build_composite_key(sec_key, &key)?;
ops.push(SecondaryKeyOp::Remove(composite_key));
}
} else {
for sec_key in secondary_keys.values() {
let composite_key = self.build_composite_key(sec_key, &key)?;
self.secondary_tree.remove(composite_key)?;
}
}
}
Ok(Some(model))
}
Err(_) => Ok(None),
}
}
None => Ok(None),
}
}
fn build_composite_key(
&self,
secondary_key: &<M::Keys as NetabaseModelTraitKey<D>>::SecondaryKey,
primary_key: &<M::Keys as NetabaseModelTraitKey<D>>::PrimaryKey,
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let mut composite_key = bincode::encode_to_vec(secondary_key, bincode::config::standard())?;
let prim_key_bytes = bincode::encode_to_vec(primary_key, bincode::config::standard())?;
composite_key.extend_from_slice(&prim_key_bytes);
Ok(composite_key)
}
}