libkv 0.1.0

A library for building data structures atop key-value stores.
Documentation
use std::{borrow::Cow, marker::PhantomData, ops::Bound};

use crate::{
    DataStructure, DsIter, IterableStorage, KeySerde, KeySerializeError, NonTerminal, Order,
};

pub struct Map<'a, K: KeySerde, V: DataStructure> {
    prefix: Cow<'a, [u8]>,
    _marker: PhantomData<(K, V)>,
}
impl<'a, K: KeySerde, V: DataStructure> DataStructure for Map<'a, K, V> {
    type Key = (K, Option<V::Key>);
    type DsType = NonTerminal;
    type Enc = V::Enc;
    type Value = V::Value;

    fn with_prefix(prefix: Vec<u8>) -> Self {
        Self {
            prefix: Cow::Owned(prefix),
            _marker: PhantomData,
        }
    }

    fn should_skip_key(key: &Self::Key) -> bool {
        key.1.as_ref().map_or(false, V::should_skip_key)
    }
}

impl<K: KeySerde, V: DataStructure> Map<'static, K, V> {
    pub const fn new(key: &'static [u8]) -> Self {
        Self {
            prefix: Cow::Borrowed(key),
            _marker: PhantomData,
        }
    }
}

impl<'a, K: KeySerde, V: DataStructure> Map<'a, K, V> {
    fn key(&self, key: &K) -> Result<Vec<u8>, KeySerializeError> {
        let encoded = key.encode()?;
        let full = [self.prefix.as_ref(), &encoded].concat();

        Ok(full)
    }

    pub fn prefix(&self) -> &[u8] {
        self.prefix.as_ref()
    }

    pub fn at(&self, key: impl Into<K>) -> Result<V, KeySerializeError> {
        Ok(V::with_prefix(self.key(&key.into())?))
    }

    pub fn range<'b, S: IterableStorage>(
        &self,
        storage: &'b S,
        start: Bound<K>,
        end: Bound<K>,
        order: Order,
    ) -> Result<DsIter<'b, Self>, KeySerializeError> {
        let start = match start {
            Bound::Included(k) => Bound::Included(self.key(&k)?),
            Bound::Excluded(k) => Bound::Excluded(self.key(&k)?),
            Bound::Unbounded => Bound::Unbounded,
        };
        let end = match end {
            Bound::Included(k) => Bound::Included(self.key(&k)?),
            Bound::Excluded(k) => Bound::Excluded(self.key(&k)?),
            Bound::Unbounded => Bound::Unbounded,
        };
        let iter = storage.iter(start, end, order)?;
        Ok(DsIter::new(self.prefix.to_vec(), iter))
    }
}

#[cfg(test)]
mod test {
    use std::collections::{BTreeMap, HashMap};

    use crate::{mock::DisplayEncoding, Item};

    use super::*;

    #[test]
    fn test_map() {
        type MapT = Map<'static, String, Item<'static, String, DisplayEncoding>>;
        const MAP: MapT = Map::new(b"foo");

        let mut storage: HashMap<Vec<u8>, Vec<u8>> = HashMap::new();
        let acc = MAP.at("bar").unwrap();
        assert_eq!(acc.may_load(&storage), Ok(None));
        acc.save(&mut storage, &"baz".to_string()).unwrap();
        assert_eq!(acc.may_load(&storage), Ok(Some("baz".to_string())));

        type NestedT = Map<'static, usize, MapT>;
        const NESTED: NestedT = Map::new(b"nested");
        let acc = NESTED.at(42usize).unwrap().at("qux").unwrap();
        assert_eq!(acc.may_load(&storage), Ok(None));
        acc.save(&mut storage, &"quux".to_string()).unwrap();
        assert_eq!(acc.may_load(&storage), Ok(Some("quux".to_string())));

        println!("{storage:?}");

        dbg!(std::any::type_name_of_val(&MAP));
        dbg!(std::any::type_name::<<MapT as DataStructure>::Key>());
        dbg!(std::any::type_name_of_val(&NESTED));
        dbg!(std::any::type_name::<<NestedT as DataStructure>::Key>());
    }

    #[test]
    fn test_map_iter() {
        let mut storage: BTreeMap<Vec<u8>, Vec<u8>> = BTreeMap::new();
        const MAP: Map<String, Item<String, DisplayEncoding>> = Map::new(b"foo");

        for i in 0..10 {
            let key = format!("k{}", i);
            let item = MAP.at(key.clone()).unwrap();
            item.save(&mut storage, &format!("value{}", i)).unwrap();
        }

        let iter = MAP
            .range(
                &storage,
                Bound::Unbounded,
                Bound::Unbounded,
                Order::Ascending,
            )
            .unwrap();
        for item in iter {
            if let Ok((key, value)) = item {
                println!("{:?}: {:?}", key, value);
            } else {
                panic!("Error: {:?}", item);
            }
        }
    }
}