treehouse 0.1.0

A searchable typed datastore built with sled
Documentation
use crate::{err, ext, utils, Store, Object};

use std::collections::BTreeSet;

pub(crate) const TERMS_PREFIX: &'static [u8] = b"__house__\0terms\0";

pub struct Term<'a> {
    pub field: &'a str,
    pub value: &'a [u8],
}

impl<'a> Term<'a> {
    pub(crate) fn flatten(self) -> Vec<u8> {
        let mut out = Vec::with_capacity(self.field.len() + self.value.len() + 1);
        out.extend(self.field.as_bytes());
        out.push(0);
        out.extend(self.value);
        out
    }
}

pub trait Queryable {
    fn query_terms(&self) -> Vec<Term>;
}

pub trait TransactionalQuery<T: ext::MaybeTransactional> {
    fn matching_ids(&self, tree: &T) -> Result<BTreeSet<u64>, T::Error>;
}

pub trait Query {
    fn matching_ids(&self, tree: &sled::Tree) -> err::Result<BTreeSet<u64>>;
}

impl<U> Query for U where U: TransactionalQuery<sled::Tree> {
    fn matching_ids(&self, tree: &sled::Tree) -> err::Result<BTreeSet<u64>> {
        TransactionalQuery::matching_ids(self, tree).map_err(err::Error::from)
    }
}

pub struct StrEquals<'a, D>(pub &'a str, pub D);

impl<'a, T, D> TransactionalQuery<T> for StrEquals<'a, D> 
    where T: ext::MaybeTransactional,
    T::Error: From<err::Error>,
    D: std::fmt::Display,
{
    fn matching_ids(&self, tree: &T) -> Result<BTreeSet<u64>, T::Error> {

        use std::io::Write;
        
        let mut key = Vec::new();

        write!(&mut key, "{}\0{}", &self.0, &self.1).map_err(err::Error::from)?;

        let mut out = BTreeSet::new();

        if let Some(val) = tree.get(key)? {
            let mut ids: Vec<u64> = utils::deserialize(&val)?;
            out.extend(&ids);
        }

        Ok(out)
    }
}

pub struct Results<'a, T> {
    pub(crate) store: &'a Store<T>,
    pub(crate) matching_ids: BTreeSet<u64>,
}

impl<'a, T: Queryable + serde::Serialize + serde::de::DeserializeOwned> Results<'a, T> {

    pub fn first(self) -> err::Result<Option<Object<T>>> {
        let Self { store, matching_ids } = self;
        Ok(matching_ids.into_iter().next()
            .map(|id| store.find(id) )
            .transpose()?
            .and_then(|x| x))
    }

    pub fn all(self) -> err::Result<Vec<Object<T>>> {
        let Self { store, matching_ids } = self;
        let mut out = Vec::with_capacity(matching_ids.len());

        for id in matching_ids.into_iter() {
            if let Some(obj) = store.find(id)? {
                out.push(obj);
            }
        }

        Ok(out)
    }

}

pub struct TransactionalResults<'a, T> {
    pub(crate) store: &'a crate::TransactionalStore<'a, T>,
    pub(crate) matching_ids: BTreeSet<u64>,
}

impl<'a, T: Queryable + serde::Serialize + serde::de::DeserializeOwned> TransactionalResults<'a, T> {

    pub fn first(self) -> Result<Option<Object<T>>, sled::transaction::ConflictableTransactionError<err::Error>> {
        let Self { store, matching_ids } = self;
        Ok(matching_ids.into_iter().next()
            .map(|id| store.find(id) )
            .transpose()?
            .and_then(|x| x))
    }

    pub fn all(self) -> Result<Vec<Object<T>>, sled::transaction::ConflictableTransactionError<err::Error>> {
        let Self { store, matching_ids } = self;
        let mut out = Vec::with_capacity(matching_ids.len());

        for id in matching_ids.into_iter() {
            if let Some(obj) = store.find(id)? {
                out.push(obj);
            }
        }

        Ok(out)
    }

}