reindeer 0.3.0

A small entity-based embedded database with a minimal no-SQL relationnal model, written in pure Rust.
Documentation
use hashers::fx_hash::FxHasher;
use serde_derive::{Deserialize, Serialize};
use std::collections::HashMap;
use std::hash::BuildHasherDefault;

use crate::entity::AsBytes;
use crate::Entity;

use super::DeletionBehaviour;

#[doc(hidden)]
pub type RelationMap = HashMap<String, Vec<RelationDescriptor>, BuildHasherDefault<FxHasher>>;

#[doc(hidden)]
#[derive(Serialize, Deserialize, Default)]
pub struct EntityRelations {
    pub related_entities: RelationMap,
}

#[doc(hidden)]
#[derive(Serialize, Deserialize, PartialEq, Eq)]
pub struct RelationDescriptor {
    pub key: Vec<u8>,
    pub deletion_behaviour: DeletionBehaviour,
    pub name: Option<String>,
}

impl RelationDescriptor {
    fn new(
        key: &[u8],
        deletion_behaviour: DeletionBehaviour,
        name: Option<&str>,
    ) -> RelationDescriptor {
        RelationDescriptor {
            key: key.to_owned(),
            deletion_behaviour,
            name: name.map(|s| s.to_owned()),
        }
    }
}

#[doc(hidden)]
#[derive(Serialize, Deserialize, Default)]
pub struct FamilyDescriptor {
    pub tree_name: String,
    pub sibling_trees: Vec<(String, DeletionBehaviour)>,
    pub child_trees: Vec<(String, DeletionBehaviour)>,
}

#[doc(hidden)]
impl EntityRelations {
    pub fn add_related<E: Entity>(
        &mut self,
        e: &E,
        behaviour: DeletionBehaviour,
        name: Option<&str>,
    ) {
        let key = e.get_key().as_bytes();
        self.add_related_by_key(E::store_name(), &key, behaviour, name);
    }

    pub fn add_related_by_key(
        &mut self,
        tree_name: &str,
        key: &[u8],
        behaviour: DeletionBehaviour,
        name: Option<&str>,
    ) {
        if let Some(v) = self.related_entities.get_mut(tree_name) {
            let relation_descriptor = RelationDescriptor::new(key, behaviour, name);
            if !v.contains(&relation_descriptor) {
                v.push(relation_descriptor);
            }
        } else {
            self.related_entities.insert(
                String::from(tree_name),
                vec![RelationDescriptor::new(key, behaviour, name)],
            );
        }
    }

    pub fn remove_related_by_key<E: Entity>(&mut self, e: &[u8]) {
        self.remove_related_by_key_and_tree_name(E::store_name(), e)
    }

    pub fn remove_related_by_key_with_name<E: Entity>(&mut self, e: &[u8], name: &str) {
        self.remove_related_by_key_and_tree_name_with_name(E::store_name(), e, name)
    }

    pub fn remove_related_by_key_and_tree_name(&mut self, tree: &str, e: &[u8]) {
        if let Some(v) = self.related_entities.get_mut(tree) {
            v.retain(|rd| rd.key.to_ascii_lowercase() != e.to_ascii_lowercase());
        }
    }

    pub fn remove_related_by_key_and_tree_name_with_name(
        &mut self,
        tree: &str,
        e: &[u8],
        name: &str,
    ) {
        if let Some(v) = self.related_entities.get_mut(tree) {
            v.retain(|rd| {
                rd.key.to_ascii_lowercase() != e.to_ascii_lowercase()
                    && if let Some(r_name) = &rd.name {
                        name == r_name
                    } else {
                        false
                    }
            });
        }
    }

    pub fn replace_id(&mut self, tree: &str, old_id: &[u8], new_id: &[u8]) {
        self.related_entities
            .iter_mut()
            .for_each(|(tree_name, entities)| {
                if tree_name == tree {
                    entities.iter_mut().for_each(|desc| {
                        if desc.key == old_id {
                            desc.key = new_id.to_owned();
                        }
                    });
                }
            })
    }
}

#[doc(hidden)]
impl Entity for FamilyDescriptor {
    type Key = String;

    fn store_name() -> &'static str {
        "__$family_rel"
    }

    fn get_key(&self) -> &Self::Key {
        &self.tree_name
    }

    fn set_key(&mut self, key: &Self::Key) {
        self.tree_name = key.clone();
    }
}