1use crate::node::{
2 validate_app_memory_id, validate_memory_id_in_range, validate_memory_id_not_reserved,
3 validate_stable_key, validate_stable_key_segment,
4};
5use crate::prelude::*;
6
7#[derive(Clone, Debug, Serialize)]
17pub struct Store {
18 def: Def,
19 ident: &'static str,
20 name: &'static str,
21 canister: &'static str,
22 data_memory_id: u8,
23 index_memory_id: u8,
24 schema_memory_id: u8,
25}
26
27impl Store {
28 #[must_use]
29 pub const fn new(
30 def: Def,
31 ident: &'static str,
32 store_name: &'static str,
33 canister: &'static str,
34 data_memory_id: u8,
35 index_memory_id: u8,
36 schema_memory_id: u8,
37 ) -> Self {
38 Self {
39 def,
40 ident,
41 name: store_name,
42 canister,
43 data_memory_id,
44 index_memory_id,
45 schema_memory_id,
46 }
47 }
48
49 #[must_use]
50 pub const fn def(&self) -> &Def {
51 &self.def
52 }
53
54 #[must_use]
55 pub const fn ident(&self) -> &'static str {
56 self.ident
57 }
58
59 #[must_use]
60 pub const fn store_name(&self) -> &'static str {
61 self.name
62 }
63
64 #[must_use]
65 pub const fn canister(&self) -> &'static str {
66 self.canister
67 }
68
69 #[must_use]
70 pub const fn data_memory_id(&self) -> u8 {
71 self.data_memory_id
72 }
73
74 #[must_use]
75 pub const fn index_memory_id(&self) -> u8 {
76 self.index_memory_id
77 }
78
79 #[must_use]
80 pub const fn schema_memory_id(&self) -> u8 {
81 self.schema_memory_id
82 }
83
84 #[must_use]
85 pub fn data_allocation(&self, memory_namespace: &str) -> StableMemoryAllocation {
86 self.allocation(memory_namespace, StoreMemoryRole::Data)
87 }
88
89 #[must_use]
90 pub fn index_allocation(&self, memory_namespace: &str) -> StableMemoryAllocation {
91 self.allocation(memory_namespace, StoreMemoryRole::Index)
92 }
93
94 #[must_use]
95 pub fn schema_allocation(&self, memory_namespace: &str) -> StableMemoryAllocation {
96 self.allocation(memory_namespace, StoreMemoryRole::Schema)
97 }
98
99 #[must_use]
100 pub fn allocation(
101 &self,
102 memory_namespace: &str,
103 role: StoreMemoryRole,
104 ) -> StableMemoryAllocation {
105 let memory_id = match role {
106 StoreMemoryRole::Data => self.data_memory_id,
107 StoreMemoryRole::Index => self.index_memory_id,
108 StoreMemoryRole::Schema => self.schema_memory_id,
109 };
110
111 StableMemoryAllocation::new(
112 memory_id,
113 stable_memory_key(memory_namespace, self.store_name(), role.as_str()),
114 None,
115 None,
116 )
117 }
118}
119
120#[derive(Clone, Copy, Debug, Eq, PartialEq)]
121pub enum StoreMemoryRole {
122 Data,
123 Index,
124 Schema,
125}
126
127impl StoreMemoryRole {
128 #[must_use]
129 pub const fn as_str(self) -> &'static str {
130 match self {
131 Self::Data => "data",
132 Self::Index => "index",
133 Self::Schema => "schema",
134 }
135 }
136}
137
138#[derive(Clone, Debug, Eq, PartialEq)]
139pub struct StableMemoryAllocation {
140 memory_id: u8,
141 stable_key: String,
142 schema_version: Option<u32>,
143 schema_fingerprint: Option<String>,
144}
145
146impl StableMemoryAllocation {
147 #[must_use]
148 pub const fn new(
149 memory_id: u8,
150 stable_key: String,
151 schema_version: Option<u32>,
152 schema_fingerprint: Option<String>,
153 ) -> Self {
154 Self {
155 memory_id,
156 stable_key,
157 schema_version,
158 schema_fingerprint,
159 }
160 }
161
162 #[must_use]
163 pub const fn memory_id(&self) -> u8 {
164 self.memory_id
165 }
166
167 #[must_use]
168 pub const fn stable_key(&self) -> &str {
169 self.stable_key.as_str()
170 }
171
172 #[must_use]
173 pub const fn schema_version(&self) -> Option<u32> {
174 self.schema_version
175 }
176
177 #[must_use]
178 pub const fn schema_fingerprint(&self) -> Option<&str> {
179 match &self.schema_fingerprint {
180 Some(value) => Some(value.as_str()),
181 None => None,
182 }
183 }
184
185 #[must_use]
186 pub fn same_identity_as(&self, other: &Self) -> bool {
187 self.memory_id == other.memory_id && self.stable_key == other.stable_key
188 }
189}
190
191#[must_use]
192pub fn stable_memory_key(memory_namespace: &str, store_name: &str, role: &str) -> String {
193 format!("icydb.{memory_namespace}.{store_name}.{role}.v1")
194}
195
196impl MacroNode for Store {
197 fn as_any(&self) -> &dyn std::any::Any {
198 self
199 }
200}
201
202impl ValidateNode for Store {
203 fn validate(&self) -> Result<(), ErrorTree> {
204 let mut errs = ErrorTree::new();
205 let schema = schema_read();
206
207 match schema.cast_node::<Canister>(self.canister()) {
208 Ok(canister) => {
209 validate_stable_key_segment(&mut errs, "store store_name", self.store_name());
210
211 validate_memory_id_in_range(
213 &mut errs,
214 "data_memory_id",
215 self.data_memory_id(),
216 canister.memory_min(),
217 canister.memory_max(),
218 );
219 validate_app_memory_id(&mut errs, "data_memory_id", self.data_memory_id());
220 validate_memory_id_not_reserved(&mut errs, "data_memory_id", self.data_memory_id());
221 validate_stable_key(
222 &mut errs,
223 "data stable key",
224 self.data_allocation(canister.memory_namespace())
225 .stable_key(),
226 );
227
228 validate_memory_id_in_range(
230 &mut errs,
231 "index_memory_id",
232 self.index_memory_id(),
233 canister.memory_min(),
234 canister.memory_max(),
235 );
236 validate_app_memory_id(&mut errs, "index_memory_id", self.index_memory_id());
237 validate_memory_id_not_reserved(
238 &mut errs,
239 "index_memory_id",
240 self.index_memory_id(),
241 );
242 validate_stable_key(
243 &mut errs,
244 "index stable key",
245 self.index_allocation(canister.memory_namespace())
246 .stable_key(),
247 );
248
249 validate_memory_id_in_range(
251 &mut errs,
252 "schema_memory_id",
253 self.schema_memory_id(),
254 canister.memory_min(),
255 canister.memory_max(),
256 );
257 validate_app_memory_id(&mut errs, "schema_memory_id", self.schema_memory_id());
258 validate_memory_id_not_reserved(
259 &mut errs,
260 "schema_memory_id",
261 self.schema_memory_id(),
262 );
263 validate_stable_key(
264 &mut errs,
265 "schema stable key",
266 self.schema_allocation(canister.memory_namespace())
267 .stable_key(),
268 );
269
270 if self.data_memory_id() == self.index_memory_id() {
272 err!(
273 errs,
274 "data_memory_id and index_memory_id must differ (both are {})",
275 self.data_memory_id(),
276 );
277 }
278 if self.data_memory_id() == self.schema_memory_id() {
279 err!(
280 errs,
281 "data_memory_id and schema_memory_id must differ (both are {})",
282 self.data_memory_id(),
283 );
284 }
285 if self.index_memory_id() == self.schema_memory_id() {
286 err!(
287 errs,
288 "index_memory_id and schema_memory_id must differ (both are {})",
289 self.index_memory_id(),
290 );
291 }
292 }
293 Err(e) => errs.add(e),
294 }
295
296 errs.result()
297 }
298}
299
300impl VisitableNode for Store {
301 fn route_key(&self) -> String {
302 self.def().path()
303 }
304
305 fn drive<V: Visitor>(&self, v: &mut V) {
306 self.def().accept(v);
307 }
308}