haematite 0.1.0

Content-addressed, branchable, actor-native storage engine
Documentation
use std::collections::HashMap;
use std::convert::Infallible;
use std::fmt::Debug;

use crate::tree::{Hash, Node};

pub trait NodeStore: Debug {
    type Error: std::error::Error;

    fn get(&self, hash: &Hash) -> Result<Option<Node>, Self::Error>;

    fn put(&mut self, node: &Node) -> Result<Hash, Self::Error>;
}

#[derive(Debug, Default, Clone)]
pub struct MemoryStore {
    nodes: HashMap<Hash, Vec<u8>>,
}

impl MemoryStore {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn get(&self, hash: &Hash) -> Option<Node> {
        self.nodes
            .get(hash)
            .and_then(|serialised| Node::deserialise(serialised).ok())
    }

    pub fn put(&mut self, node: &Node) -> Hash {
        let hash = node.hash();
        self.nodes.insert(hash, node.serialise());
        hash
    }
}

impl NodeStore for MemoryStore {
    type Error = Infallible;

    fn get(&self, hash: &Hash) -> Result<Option<Node>, Self::Error> {
        Ok(Self::get(self, hash))
    }

    fn put(&mut self, node: &Node) -> Result<Hash, Self::Error> {
        Ok(Self::put(self, node))
    }
}

#[cfg(test)]
mod tests {
    use super::MemoryStore;
    use crate::tree::{Hash, LeafNode, Node, NodeError};

    fn node() -> Result<Node, NodeError> {
        LeafNode::new(vec![(b"a".to_vec(), b"one".to_vec())]).map(Node::Leaf)
    }

    #[test]
    fn put_stores_node_under_content_hash() -> Result<(), NodeError> {
        let node = node()?;
        let mut store = MemoryStore::new();
        let hash = store.put(&node);

        assert_eq!(hash, node.hash());
        assert_eq!(store.get(&hash), Some(node));
        Ok(())
    }

    #[test]
    fn get_returns_none_for_unknown_hash() {
        let store = MemoryStore::new();
        assert_eq!(store.get(&Hash::from_bytes([7; 32])), None);
    }
}