libkv/
container.rs

1use std::marker::PhantomData;
2
3use crate::{Codec, Decodable, Encoding, Iter, KeySerde, StorageError};
4
5/// Trait representing an arbitrary data structure that can be used in a
6/// Key-Value store.
7///
8/// The trait requires the following associated types:
9/// - `Key`: The type of the key used to index the data structure.
10/// - `Enc`: The encoding used to serialize and deserialize the data structure.
11/// - `Value`: The value type, that can be (de)serialized using the `Enc` encoding.
12/// - `DsType`: A type that indicates whether the data structure is terminal or
13///     non-terminal.
14///
15/// The trait also requires the following methods:
16/// - `with_prefix`: A constructor that takes a byte-prefix, and returns a handle
17///    to the data structure with that prefix.
18/// - `should_skip_key`: A method that takes a key and returns whether the key
19///   should be skipped when iterating over the data structure.
20pub trait DataStructure {
21    /// The type of the key used to index the data structure.
22    type Key: KeySerde;
23    /// The encoding used to serialize and deserialize the data structure.
24    type Enc: Encoding;
25    /// The value type, that can be (de)serialized using the `Enc` encoding.
26    type Value: Codec<Self::Enc>;
27    /// A type that indicates whether the data structure is terminal or non-terminal.
28    type DsType: sealed::ContainerType;
29
30    /// Constructor that takes a byte-prefix, and returns a handle to the data structure
31    fn with_prefix(prefix: Vec<u8>) -> Self
32    where
33        Self: Sized;
34
35    /// Returns whether the given key should be skipped when iterating over the data structure.
36    fn should_skip_key(key: &Self::Key) -> bool;
37}
38
39/// A trait representing a data structure that is a container of other data structures.
40///
41/// Most data structures are non-terminal, meaning they contain other data structures.
42/// Since we represent actual values as the `Item` structure, even "simple" data structures
43/// like a Key-Value Map are considered non-terminal, and thus are marked as `Container`.
44pub trait Container: DataStructure<DsType = NonTerminal> {
45    type Inner: DataStructure;
46}
47
48mod sealed {
49    pub trait ContainerType: Sized {}
50}
51
52pub struct Terminal {}
53impl sealed::ContainerType for Terminal {}
54pub struct NonTerminal {}
55impl sealed::ContainerType for NonTerminal {}
56
57pub struct DsIter<'a, D: DataStructure> {
58    _marker: PhantomData<D>,
59    prefix: Vec<u8>,
60    iter: Iter<'a, (Vec<u8>, Vec<u8>)>,
61}
62
63impl<'a, D: DataStructure> DsIter<'a, D> {
64    pub const fn new(prefix: Vec<u8>, iter: Iter<'a, (Vec<u8>, Vec<u8>)>) -> Self {
65        Self {
66            _marker: PhantomData,
67            prefix,
68            iter,
69        }
70    }
71}
72
73impl<'a, D: DataStructure> Iterator for DsIter<'a, D> {
74    type Item = Result<(D::Key, D::Value), StorageError<D::Enc>>;
75
76    fn next(&mut self) -> Option<Self::Item> {
77        loop {
78            let (mut key_bytes, val_bytes) = self.iter.next()?;
79            if !key_bytes.starts_with(&self.prefix) {
80                return None;
81            }
82            let key_bytes = key_bytes.split_off(self.prefix.len());
83            let key = <<D as DataStructure>::Key as KeySerde>::decode(&mut key_bytes.as_slice())
84                .map(|k| (D::should_skip_key(&k), k));
85
86            match key {
87                Ok((true, _)) => continue,
88                Ok((false, key)) => {
89                    let val = <D::Value as Decodable<D::Enc>>::decode(&val_bytes)
90                        .map_err(StorageError::ValueDeserialize);
91                    return Some(val.map(|val| (key, val)));
92                }
93                Err(e) => return Some(Err(StorageError::KeyDeserialize(e))),
94            }
95        }
96    }
97}