icydb_core/db/store/
data.rs

1use crate::{Key, db::store::StoreRegistry, traits::EntityKind};
2use candid::CandidType;
3use canic::{
4    cdk::structures::{BTreeMap, DefaultMemoryImpl, memory::VirtualMemory},
5    impl_storable_bounded,
6};
7use derive_more::{Deref, DerefMut};
8use serde::{Deserialize, Serialize};
9use std::fmt::{self, Display};
10
11///
12/// DataStoreRegistry
13///
14
15#[derive(Deref, DerefMut)]
16pub struct DataStoreRegistry(StoreRegistry<DataStore>);
17
18impl DataStoreRegistry {
19    #[must_use]
20    #[allow(clippy::new_without_default)]
21    pub fn new() -> Self {
22        Self(StoreRegistry::new())
23    }
24}
25
26///
27/// DataStore
28///
29
30#[derive(Deref, DerefMut)]
31pub struct DataStore(BTreeMap<DataKey, Vec<u8>, VirtualMemory<DefaultMemoryImpl>>);
32
33impl DataStore {
34    #[must_use]
35    pub fn init(memory: VirtualMemory<DefaultMemoryImpl>) -> Self {
36        Self(BTreeMap::init(memory))
37    }
38
39    pub fn memory_bytes(&self) -> u64 {
40        self.iter()
41            .map(|entry| u64::from(DataKey::STORABLE_MAX_SIZE) + entry.value().len() as u64)
42            .sum()
43    }
44}
45
46///
47/// DataRow
48///
49
50pub type DataRow = (DataKey, Vec<u8>);
51
52///
53/// DataKey
54///
55
56#[derive(
57    CandidType, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize,
58)]
59pub struct DataKey {
60    entity_id: u64,
61    key: Key,
62}
63
64impl DataKey {
65    pub const STORABLE_MAX_SIZE: u32 = 160;
66
67    #[must_use]
68    pub fn new<E: EntityKind>(key: impl Into<Key>) -> Self {
69        Self {
70            entity_id: E::ENTITY_ID,
71            key: key.into(),
72        }
73    }
74
75    #[must_use]
76    pub const fn lower_bound<E: EntityKind>() -> Self {
77        Self {
78            entity_id: E::ENTITY_ID,
79            key: Key::lower_bound(),
80        }
81    }
82
83    #[must_use]
84    pub const fn upper_bound<E: EntityKind>() -> Self {
85        Self {
86            entity_id: E::ENTITY_ID,
87            key: Key::upper_bound(),
88        }
89    }
90
91    /// Return the primary key component of this data key.
92    #[must_use]
93    pub const fn key(&self) -> Key {
94        self.key
95    }
96
97    /// Entity identifier (stable, compile-time constant per entity type).
98    #[must_use]
99    pub const fn entity_id(&self) -> u64 {
100        self.entity_id
101    }
102
103    /// Compute the on-disk size used by a single data entry from its value length.
104    /// Includes the bounded `DataKey` size and the value bytes.
105    #[must_use]
106    pub const fn entry_size_bytes(value_len: u64) -> u64 {
107        Self::STORABLE_MAX_SIZE as u64 + value_len
108    }
109
110    #[must_use]
111    pub fn max_storable() -> Self {
112        Self {
113            entity_id: u64::MAX,
114            key: Key::max_storable(),
115        }
116    }
117}
118
119impl Display for DataKey {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        write!(f, "#{} ({})", self.entity_id, self.key)
122    }
123}
124
125impl From<DataKey> for Key {
126    fn from(key: DataKey) -> Self {
127        key.key()
128    }
129}
130
131impl_storable_bounded!(DataKey, Self::STORABLE_MAX_SIZE, false);
132
133///
134/// TESTS
135///
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140    use crate::traits::Storable;
141
142    #[test]
143    fn data_key_max_size_is_bounded() {
144        let data_key = DataKey::max_storable();
145        let size = Storable::to_bytes(&data_key).len();
146
147        assert!(
148            size <= DataKey::STORABLE_MAX_SIZE as usize,
149            "serialized DataKey too large: got {size} bytes (limit {})",
150            DataKey::STORABLE_MAX_SIZE
151        );
152    }
153}