lib3h_persistence_mem 0.0.1-alpha2

lib3h persistence for content addressable storage and entity attribute value indexes. A simple, thread-safe in memory store.
use lib3h_persistence_api::{
    eav::{
        increment_key_till_no_collision, Attribute, EaviQuery, EntityAttributeValueIndex,
        EntityAttributeValueStorage,
    },
    error::PersistenceResult,
};
use std::{
    collections::BTreeSet,
    sync::{Arc, RwLock},
};

use uuid::Uuid;

#[derive(Clone, Debug)]
pub struct EavMemoryStorage<A: Attribute> {
    storage: Arc<RwLock<BTreeSet<EntityAttributeValueIndex<A>>>>,
    id: Uuid,
}

impl<A: Attribute> PartialEq for EavMemoryStorage<A> {
    fn eq(&self, other: &EavMemoryStorage<A>) -> bool {
        self.id == other.id
    }
}

impl<A: Attribute> Default for EavMemoryStorage<A> {
    fn default() -> EavMemoryStorage<A> {
        EavMemoryStorage {
            storage: Arc::new(RwLock::new(BTreeSet::new())),
            id: Uuid::new_v4(),
        }
    }
}

impl<A: Attribute> EavMemoryStorage<A> {
    pub fn new() -> EavMemoryStorage<A> {
        Default::default()
    }
}

impl<A: Attribute> EntityAttributeValueStorage<A> for EavMemoryStorage<A>
where
    A: Send + Sync,
{
    fn add_eavi(
        &mut self,
        eav: &EntityAttributeValueIndex<A>,
    ) -> PersistenceResult<Option<EntityAttributeValueIndex<A>>> {
        let mut map = self.storage.write()?;
        let new_eav = increment_key_till_no_collision(eav.clone(), map.clone())?;
        map.insert(new_eav.clone());
        Ok(Some(new_eav.clone()))
    }

    fn fetch_eavi(
        &self,
        query: &EaviQuery<A>,
    ) -> PersistenceResult<BTreeSet<EntityAttributeValueIndex<A>>> {
        let map = self.storage.read()?;
        let iter = map.iter().cloned();
        Ok(query.run(iter))
    }
}

#[cfg(test)]
pub mod tests {
    use crate::eav::memory::EavMemoryStorage;
    use lib3h_persistence_api::{
        cas::{
            content::{AddressableContent, ExampleAddressableContent},
            storage::EavTestSuite,
        },
        eav::ExampleAttribute,
        json::RawString,
    };

    #[test]
    fn memory_eav_round_trip() {
        let entity_content =
            ExampleAddressableContent::try_from_content(&RawString::from("foo").into()).unwrap();
        let attribute = ExampleAttribute::WithPayload("favourite-color".to_string());
        let value_content =
            ExampleAddressableContent::try_from_content(&RawString::from("blue").into()).unwrap();
        EavTestSuite::test_round_trip(
            EavMemoryStorage::new(),
            entity_content,
            attribute,
            value_content,
        )
    }

    #[test]
    fn memory_eav_one_to_many() {
        let eav_storage = EavMemoryStorage::new();
        EavTestSuite::test_one_to_many::<
            ExampleAddressableContent,
            ExampleAttribute,
            EavMemoryStorage<ExampleAttribute>,
        >(eav_storage, &ExampleAttribute::default())
    }

    #[test]
    fn memory_eav_many_to_one() {
        let eav_storage = EavMemoryStorage::new();
        EavTestSuite::test_many_to_one::<
            ExampleAddressableContent,
            ExampleAttribute,
            EavMemoryStorage<ExampleAttribute>,
        >(eav_storage, &ExampleAttribute::default())
    }

    #[test]
    fn example_eav_range() {
        let eav_storage = EavMemoryStorage::new();
        EavTestSuite::test_range::<
            ExampleAddressableContent,
            ExampleAttribute,
            EavMemoryStorage<ExampleAttribute>,
        >(eav_storage, &ExampleAttribute::default());
    }

    #[test]
    fn file_eav_prefixes() {
        let eav_storage = EavMemoryStorage::new();
        EavTestSuite::test_multiple_attributes::<
            ExampleAddressableContent,
            ExampleAttribute,
            EavMemoryStorage<ExampleAttribute>,
        >(
            eav_storage,
            vec!["a_", "b_", "c_", "d_"]
                .into_iter()
                .map(|p| ExampleAttribute::WithPayload(p.to_string() + "one_to_many"))
                .collect(),
        );
    }

}