metaldb 1.0.0

Persistent storage implementation based on RocksDB.
Documentation
//! An implementation of a key-value map.
//!
//! `MapIndex` requires that keys implement the [`BinaryKey`] trait and values implement
//! the [`BinaryValue`] trait. The given section contains methods related to
//! `MapIndex` and iterators over the items of this map.

use std::{borrow::Borrow, marker::PhantomData};

use crate::{
    access::{Access, AccessError, FromAccess},
    indexes::iter::{Entries, IndexIterator, Keys, Values},
    views::{IndexAddress, IndexType, RawAccess, RawAccessMut, View, ViewWithMetadata},
    BinaryKey, BinaryValue,
};

/// A map of keys and values. Access to the elements of this map is obtained using the keys.
///
/// `MapIndex` requires that keys implement the [`BinaryKey`] trait and values implement
/// the [`BinaryValue`] trait.
///
/// [`BinaryKey`]: ../trait.BinaryKey.html
/// [`BinaryValue`]: ../trait.BinaryValue.html
#[derive(Debug)]
pub struct MapIndex<T: RawAccess, K: ?Sized, V> {
    base: View<T>,
    _k: PhantomData<K>,
    _v: PhantomData<V>,
}

impl<T, K, V> FromAccess<T> for MapIndex<T::Base, K, V>
where
    T: Access,
    K: BinaryKey + ?Sized,
    V: BinaryValue,
{
    fn from_access(access: T, addr: IndexAddress) -> Result<Self, AccessError> {
        let view = access.get_or_create_view(addr, IndexType::Map)?;
        Ok(Self::new(view))
    }
}

impl<T, K, V> MapIndex<T, K, V>
where
    T: RawAccess,
    K: BinaryKey + ?Sized,
    V: BinaryValue,
{
    fn new(view: ViewWithMetadata<T>) -> Self {
        let base = view.into();
        Self {
            base,
            _v: PhantomData,
            _k: PhantomData,
        }
    }

    /// Returns a value corresponding to the key.
    ///
    /// # Examples
    ///
    /// ```
    /// use metaldb::{access::CopyAccessExt, TemporaryDB, Database, MapIndex};
    ///
    /// let db = TemporaryDB::default();
    /// let fork = db.fork();
    /// let mut index = fork.get_map("name");
    /// assert!(index.get(&1).is_none());
    ///
    /// index.put(&1, 2);
    /// assert_eq!(Some(2), index.get(&1));
    /// ```
    pub fn get(&self, key: &K) -> Option<V> {
        self.base.get(key)
    }

    /// Returns values corresponding to the keys.
    ///
    /// # Examples
    ///
    /// ```
    /// use metaldb::{access::CopyAccessExt, TemporaryDB, Database, MapIndex};
    ///
    /// let db = TemporaryDB::default();
    /// let fork = db.fork();
    /// let mut index = fork.get_map("name");
    /// assert!(index.get(&1).is_none());
    ///
    /// index.put(&1, 2);
    /// index.put(&2, 3);
    /// assert_eq!(vec![Some(2), Some(3)], index.multi_get(&[1, 2]));
    /// ```
    pub fn multi_get<I>(&self, keys: I) -> Vec<Option<V>>
    where
        I: IntoIterator,
        I::Item: Borrow<K>,
    {
        self.base.multi_get(keys)
    }

    /// Returns `true` if the map contains a value corresponding to the specified key.
    ///
    /// # Examples
    ///
    /// ```
    /// use metaldb::{access::CopyAccessExt, TemporaryDB, Database, MapIndex};
    ///
    /// let db = TemporaryDB::default();
    /// let fork = db.fork();
    /// let mut index = fork.get_map("name");
    /// assert!(!index.contains(&1));
    ///
    /// index.put(&1, 2);
    /// assert!(index.contains(&1));
    /// ```
    pub fn contains(&self, key: &K) -> bool {
        self.base.contains(key)
    }

    /// Returns an iterator over the entries of the map in ascending order.
    ///
    /// # Examples
    ///
    /// ```
    /// use metaldb::{access::CopyAccessExt, TemporaryDB, Database, MapIndex};
    ///
    /// let db = TemporaryDB::default();
    /// let fork = db.fork();
    /// let index: MapIndex<_, u8, u8> = fork.get_map("name");
    ///
    /// for v in index.iter() {
    ///     println!("{:?}", v);
    /// }
    /// ```
    pub fn iter(&self) -> Entries<'_, K, V> {
        self.index_iter(None)
    }

    /// Returns an iterator over the keys of a map in ascending order.
    ///
    /// # Examples
    ///
    /// ```
    /// use metaldb::{access::CopyAccessExt, TemporaryDB, Database, MapIndex};
    ///
    /// let db = TemporaryDB::default();
    /// let fork = db.fork();
    /// let index: MapIndex<_, u8, u8> = fork.get_map("name");
    ///
    /// for key in index.keys() {
    ///     println!("{}", key);
    /// }
    /// ```
    pub fn keys(&self) -> Keys<'_, K> {
        self.iter().skip_values()
    }

    /// Returns an iterator over the values of a map in ascending order of keys.
    ///
    /// # Examples
    ///
    /// ```
    /// use metaldb::{access::CopyAccessExt, TemporaryDB, Database, MapIndex};
    ///
    /// let db = TemporaryDB::default();
    /// let fork = db.fork();
    /// let index: MapIndex<_, u8, u8> = fork.get_map("name");
    ///
    /// for val in index.values() {
    ///     println!("{}", val);
    /// }
    /// ```
    pub fn values(&self) -> Values<'_, V> {
        self.iter().skip_keys()
    }

    /// Returns an iterator over the entries of a map in ascending order starting from the
    /// specified key.
    ///
    /// # Examples
    ///
    /// ```
    /// use metaldb::{access::CopyAccessExt, TemporaryDB, Database, MapIndex};
    ///
    /// let db = TemporaryDB::default();
    /// let fork = db.fork();
    /// let index: MapIndex<_, u8, u8> = fork.get_map("name");
    ///
    /// for v in index.iter_from(&2) {
    ///     println!("{:?}", v);
    /// }
    /// ```
    pub fn iter_from(&self, from: &K) -> Entries<'_, K, V> {
        self.index_iter(Some(from))
    }

    /// Returns an iterator over the keys of a map in ascending order starting from the
    /// specified key.
    ///
    /// # Examples
    ///
    /// ```
    /// use metaldb::{access::CopyAccessExt, TemporaryDB, Database, MapIndex};
    ///
    /// let db = TemporaryDB::default();
    /// let fork = db.fork();
    /// let index: MapIndex<_, u8, u8> = fork.get_map("name");
    ///
    /// for key in index.keys_from(&2) {
    ///     println!("{}", key);
    /// }
    /// ```
    pub fn keys_from(&self, from: &K) -> Keys<'_, K> {
        self.iter_from(from).skip_values()
    }

    /// Returns an iterator over the values of a map in ascending order of keys starting from the
    /// specified key.
    ///
    /// # Examples
    ///
    /// ```
    /// use metaldb::{access::CopyAccessExt, TemporaryDB, Database, MapIndex};
    ///
    /// let db = TemporaryDB::default();
    /// let fork = db.fork();
    /// let index: MapIndex<_, u8, u8> = fork.get_map("name");
    /// for val in index.values_from(&2) {
    ///     println!("{}", val);
    /// }
    /// ```
    pub fn values_from(&self, from: &K) -> Values<'_, V> {
        self.iter_from(from).skip_keys()
    }
}

impl<T, K, V> MapIndex<T, K, V>
where
    T: RawAccessMut,
    K: BinaryKey + ?Sized,
    V: BinaryValue,
{
    /// Inserts a key-value pair into a map.
    ///
    /// # Examples
    ///
    /// ```
    /// use metaldb::{access::CopyAccessExt, TemporaryDB, Database, MapIndex};
    ///
    /// let db = TemporaryDB::default();
    /// let fork = db.fork();
    /// let mut index = fork.get_map("name");
    ///
    /// index.put(&1, 2);
    /// assert!(index.contains(&1));
    /// ```
    pub fn put(&mut self, key: &K, value: V) {
        self.base.put(key, value);
    }

    /// Removes a key from a map.
    ///
    /// # Examples
    ///
    /// ```
    /// use metaldb::{access::CopyAccessExt, TemporaryDB, Database, MapIndex};
    ///
    /// let db = TemporaryDB::default();
    /// let fork = db.fork();
    /// let mut index = fork.get_map("name");
    ///
    /// index.put(&1, 2);
    /// assert!(index.contains(&1));
    ///
    /// index.remove(&1);
    /// assert!(!index.contains(&1));
    /// ```
    pub fn remove<Q>(&mut self, key: &Q)
    where
        K: Borrow<Q>,
        Q: BinaryKey + ?Sized,
    {
        self.base.remove(key);
    }

    /// Clears a map, removing all entries.
    ///
    /// # Notes
    /// Currently, this method is not optimized to delete a large set of data. During the execution of
    /// this method, the amount of allocated memory is linearly dependent on the number of elements
    /// in the index.
    ///
    /// # Examples
    ///
    /// ```
    /// use metaldb::{access::CopyAccessExt, TemporaryDB, Database, MapIndex};
    ///
    /// let db = TemporaryDB::default();
    /// let fork = db.fork();
    /// let mut index = fork.get_map("name");
    ///
    /// index.put(&1, 2);
    /// assert!(index.contains(&1));
    ///
    /// index.clear();
    /// assert!(!index.contains(&1));
    /// ```
    pub fn clear(&mut self) {
        self.base.clear();
    }
}

impl<'a, T, K, V> IntoIterator for &'a MapIndex<T, K, V>
where
    T: RawAccess,
    K: BinaryKey + ?Sized,
    V: BinaryValue,
{
    type Item = (K::Owned, V);
    type IntoIter = Entries<'a, K, V>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

impl<T, K, V> IndexIterator for MapIndex<T, K, V>
where
    T: RawAccess,
    K: BinaryKey + ?Sized,
    V: BinaryValue,
{
    type Key = K;
    type Value = V;

    fn index_iter(&self, from: Option<&K>) -> Entries<'_, K, V> {
        Entries::new(&self.base, from)
    }
}

#[cfg(test)]
mod tests {
    use crate::{access::CopyAccessExt, Database, TemporaryDB};

    const IDX_NAME: &str = "idx_name";

    #[test]
    fn test_str_key() {
        const KEY: &str = "key_1";
        let db = TemporaryDB::default();
        let fork = db.fork();

        let mut index = fork.get_map(IDX_NAME);
        assert!(!index.contains(KEY));
        index.put(KEY, 0);
        assert!(index.contains(KEY));
        index.remove(KEY);
        assert!(!index.contains(KEY));
    }

    #[test]
    fn test_u8_slice_key() {
        const KEY: &[u8] = &[1, 2, 3];
        let db = TemporaryDB::default();
        let fork = db.fork();

        let mut index = fork.get_map(IDX_NAME);
        assert!(!index.contains(KEY));

        index.put(KEY, 0);
        assert!(index.contains(KEY));

        index.remove(KEY);
        assert!(!index.contains(KEY));
    }

    #[test]
    fn test_methods() {
        let db = TemporaryDB::default();
        let fork = db.fork();

        let mut map_index = fork.get_map(IDX_NAME);
        assert_eq!(map_index.get(&1_u8), None);
        assert!(!map_index.contains(&1_u8));

        map_index.put(&1_u8, 1_u8);
        assert_eq!(map_index.get(&1_u8), Some(1_u8));
        assert!(map_index.contains(&1_u8));

        map_index.remove(&100_u8);
        map_index.remove(&1_u8);
        assert!(!map_index.contains(&1_u8));
        assert_eq!(map_index.get(&1_u8), None);

        map_index.put(&2_u8, 2_u8);
        map_index.put(&3_u8, 3_u8);
        map_index.clear();
        assert!(!map_index.contains(&2_u8));
        assert!(!map_index.contains(&3_u8));
    }

    #[test]
    fn test_iter() {
        let db = TemporaryDB::default();
        let fork = db.fork();
        let mut map_index = fork.get_map(IDX_NAME);

        map_index.put(&1_u8, 1_u8);
        map_index.put(&2_u8, 2_u8);
        map_index.put(&3_u8, 3_u8);

        assert_eq!(
            map_index.multi_get(0..4),
            vec![None, Some(1), Some(2), Some(3)]
        );

        assert_eq!(
            map_index.iter().collect::<Vec<(u8, u8)>>(),
            vec![(1, 1), (2, 2), (3, 3)]
        );
        assert_eq!(
            map_index.iter_from(&0).collect::<Vec<(u8, u8)>>(),
            vec![(1, 1), (2, 2), (3, 3)]
        );
        assert_eq!(
            map_index.iter_from(&1).collect::<Vec<(u8, u8)>>(),
            vec![(1, 1), (2, 2), (3, 3)]
        );
        assert_eq!(
            map_index.iter_from(&2).collect::<Vec<(u8, u8)>>(),
            vec![(2, 2), (3, 3)]
        );
        assert_eq!(
            map_index.iter_from(&4).collect::<Vec<(u8, u8)>>(),
            Vec::<(u8, u8)>::new()
        );

        assert_eq!(map_index.keys().collect::<Vec<u8>>(), vec![1, 2, 3]);

        assert_eq!(map_index.keys_from(&0).collect::<Vec<u8>>(), vec![1, 2, 3]);
        assert_eq!(map_index.keys_from(&1).collect::<Vec<u8>>(), vec![1, 2, 3]);
        assert_eq!(map_index.keys_from(&2).collect::<Vec<u8>>(), vec![2, 3]);
        assert_eq!(
            map_index.keys_from(&4).collect::<Vec<u8>>(),
            Vec::<u8>::new()
        );

        assert_eq!(map_index.values().collect::<Vec<u8>>(), vec![1, 2, 3]);

        assert_eq!(
            map_index.values_from(&0).collect::<Vec<u8>>(),
            vec![1, 2, 3]
        );
        assert_eq!(
            map_index.values_from(&1).collect::<Vec<u8>>(),
            vec![1, 2, 3]
        );
        assert_eq!(map_index.values_from(&2).collect::<Vec<u8>>(), vec![2, 3]);
        assert_eq!(
            map_index.values_from(&4).collect::<Vec<u8>>(),
            Vec::<u8>::new()
        );

        map_index.remove(&1_u8);
        assert_eq!(
            map_index.iter_from(&0_u8).collect::<Vec<(u8, u8)>>(),
            vec![(2, 2), (3, 3)]
        );
        assert_eq!(
            map_index.iter_from(&1_u8).collect::<Vec<(u8, u8)>>(),
            vec![(2, 2), (3, 3)]
        );
    }

    #[test]
    fn index_as_iterator() {
        let db = TemporaryDB::default();
        let fork = db.fork();
        let mut map_index = fork.get_map(IDX_NAME);

        map_index.put(&1_u8, 1_u8);
        map_index.put(&2_u8, 2_u8);
        map_index.put(&3_u8, 3_u8);

        for (key, value) in &map_index {
            assert!(key == value);
        }
        assert_eq!((&map_index).into_iter().count(), 3);
        assert_eq!(map_index.keys().collect::<Vec<_>>(), vec![1, 2, 3]);
        assert_eq!(
            map_index.iter().collect::<Vec<_>>(),
            vec![(1, 1), (2, 2), (3, 3)]
        );

        let mut map_index = fork.get_map((IDX_NAME, &0_u8));
        map_index.put("1", 1_u8);
        map_index.put("2", 2_u8);
        map_index.put("3", 3_u8);
        for (key, value) in &map_index {
            assert_eq!(key, value.to_string());
        }
        assert_eq!((&map_index).into_iter().count(), 3);
        assert_eq!(map_index.keys().collect::<Vec<_>>(), vec!["1", "2", "3"]);
        assert_eq!(
            map_index.iter().collect::<Vec<_>>(),
            vec![
                ("1".to_owned(), 1),
                ("2".to_owned(), 2),
                ("3".to_owned(), 3)
            ]
        );
    }
}