1use crate::{
2 key::{StableKey, StableKeyError},
3 schema::SchemaMetadata,
4 slot::{MemoryManagerSlot, MemoryManagerSlotError},
5 text::{DiagnosticTextError, validate_diagnostic_text},
6};
7use serde::{Deserialize, Deserializer, Serialize, de::Error as _};
8
9#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
21#[serde(deny_unknown_fields)]
22pub struct AllocationDeclaration {
23 pub(crate) stable_key: StableKey,
25 pub(crate) slot: MemoryManagerSlot,
27 #[serde(deserialize_with = "deserialize_label")]
29 pub(crate) label: Option<String>,
30 pub(crate) schema: SchemaMetadata,
32}
33
34impl AllocationDeclaration {
35 pub fn new(
37 stable_key: impl AsRef<str>,
38 slot: MemoryManagerSlot,
39 label: Option<String>,
40 schema: SchemaMetadata,
41 ) -> Result<Self, DeclarationSnapshotError> {
42 let stable_key = StableKey::parse(stable_key).map_err(DeclarationSnapshotError::Key)?;
43 validate_label(label.as_deref())?;
44 Ok(Self {
45 stable_key,
46 slot,
47 label,
48 schema,
49 })
50 }
51
52 pub fn memory_manager(
54 stable_key: impl AsRef<str>,
55 id: u8,
56 label: impl Into<String>,
57 ) -> Result<Self, DeclarationSnapshotError> {
58 Self::memory_manager_with_schema(stable_key, id, label, SchemaMetadata::default())
59 }
60
61 pub fn memory_manager_unlabeled(
63 stable_key: impl AsRef<str>,
64 id: u8,
65 ) -> Result<Self, DeclarationSnapshotError> {
66 Self::memory_manager_unlabeled_with_schema(stable_key, id, SchemaMetadata::default())
67 }
68
69 pub fn memory_manager_with_schema(
71 stable_key: impl AsRef<str>,
72 id: u8,
73 label: impl Into<String>,
74 schema: SchemaMetadata,
75 ) -> Result<Self, DeclarationSnapshotError> {
76 let slot =
77 MemoryManagerSlot::new(id).map_err(DeclarationSnapshotError::MemoryManagerSlot)?;
78 Self::new(stable_key, slot, Some(label.into()), schema)
79 }
80
81 pub fn memory_manager_unlabeled_with_schema(
83 stable_key: impl AsRef<str>,
84 id: u8,
85 schema: SchemaMetadata,
86 ) -> Result<Self, DeclarationSnapshotError> {
87 let slot =
88 MemoryManagerSlot::new(id).map_err(DeclarationSnapshotError::MemoryManagerSlot)?;
89 Self::new(stable_key, slot, None, schema)
90 }
91
92 #[must_use]
94 pub const fn stable_key(&self) -> &StableKey {
95 &self.stable_key
96 }
97
98 #[must_use]
100 pub const fn slot(&self) -> &MemoryManagerSlot {
101 &self.slot
102 }
103
104 #[must_use]
106 pub fn label(&self) -> Option<&str> {
107 self.label.as_deref()
108 }
109
110 #[must_use]
112 pub const fn schema(&self) -> &SchemaMetadata {
113 &self.schema
114 }
115}
116
117#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
129pub struct DeclarationSnapshot {
130 declarations: Vec<AllocationDeclaration>,
132}
133
134impl<'de> Deserialize<'de> for DeclarationSnapshot {
135 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
136 #[derive(Deserialize)]
137 #[serde(rename = "DeclarationSnapshot", deny_unknown_fields)]
138 struct Snapshot {
139 #[serde(deserialize_with = "crate::cbor::deserialize_records")]
140 declarations: Vec<AllocationDeclaration>,
141 }
142
143 let snapshot = Snapshot::deserialize(deserializer)?;
144 Self::new(snapshot.declarations).map_err(D::Error::custom)
145 }
146}
147
148impl DeclarationSnapshot {
149 pub fn new(declarations: Vec<AllocationDeclaration>) -> Result<Self, DeclarationSnapshotError> {
151 validate_declaration_set(&declarations)?;
152 Ok(Self { declarations })
153 }
154
155 #[must_use]
157 pub const fn is_empty(&self) -> bool {
158 self.declarations.is_empty()
159 }
160
161 #[must_use]
163 pub const fn len(&self) -> usize {
164 self.declarations.len()
165 }
166
167 #[must_use]
169 pub fn declarations(&self) -> &[AllocationDeclaration] {
170 &self.declarations
171 }
172
173 pub(crate) fn into_declarations(self) -> Vec<AllocationDeclaration> {
174 self.declarations
175 }
176}
177
178#[non_exhaustive]
183#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
184pub enum DeclarationSnapshotError {
185 #[error("at most 255 allocation declarations are supported")]
186 TooManyDeclarations,
187 #[error(transparent)]
189 Key(StableKeyError),
190 #[error(transparent)]
192 MemoryManagerSlot(MemoryManagerSlotError),
193 #[error("stable key '{0}' is declared more than once")]
195 DuplicateStableKey(StableKey),
196 #[error("allocation slot '{0:?}' is declared more than once")]
198 DuplicateSlot(MemoryManagerSlot),
199 #[error("allocation declaration label must not be empty when present")]
201 EmptyLabel,
202 #[error("allocation declaration label must be at most 256 bytes")]
204 LabelTooLong,
205 #[error("allocation declaration label must be ASCII")]
207 NonAsciiLabel,
208 #[error("allocation declaration label must not contain ASCII control characters")]
210 ControlCharacterLabel,
211}
212
213fn validate_label(label: Option<&str>) -> Result<(), DeclarationSnapshotError> {
214 let Some(label) = label else {
215 return Ok(());
216 };
217 validate_diagnostic_text(label).map_err(|error| match error {
218 DiagnosticTextError::Empty => DeclarationSnapshotError::EmptyLabel,
219 DiagnosticTextError::TooLong => DeclarationSnapshotError::LabelTooLong,
220 DiagnosticTextError::NonAscii => DeclarationSnapshotError::NonAsciiLabel,
221 DiagnosticTextError::ControlCharacter => DeclarationSnapshotError::ControlCharacterLabel,
222 })
223}
224
225fn deserialize_label<'de, D: Deserializer<'de>>(
228 deserializer: D,
229) -> Result<Option<String>, D::Error> {
230 let label = Option::<String>::deserialize(deserializer)?;
231 validate_label(label.as_deref()).map_err(D::Error::custom)?;
232 Ok(label)
233}
234
235fn validate_declaration_set(
236 declarations: &[AllocationDeclaration],
237) -> Result<(), DeclarationSnapshotError> {
238 if declarations.len() > crate::constants::MAX_ALLOCATIONS {
239 return Err(DeclarationSnapshotError::TooManyDeclarations);
240 }
241 let duplicate_keys = crate::key::duplicate_key_positions(
242 declarations
243 .iter()
244 .enumerate()
245 .map(|(index, declaration)| (&declaration.stable_key, index))
246 .collect(),
247 );
248 let mut slots = [false; crate::constants::MAX_ALLOCATIONS];
249
250 for (index, declaration) in declarations.iter().enumerate() {
251 let occupied = &mut slots[usize::from(declaration.slot.id())];
252 if *occupied {
253 return Err(DeclarationSnapshotError::DuplicateSlot(
254 declaration.slot.clone(),
255 ));
256 }
257 *occupied = true;
258 if duplicate_keys[index] {
259 return Err(DeclarationSnapshotError::DuplicateStableKey(
260 declaration.stable_key.clone(),
261 ));
262 }
263 }
264
265 Ok(())
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271 use crate::slot::MemoryManagerSlot;
272
273 fn declaration(key: &str, id: u8) -> AllocationDeclaration {
274 AllocationDeclaration::new(
275 key,
276 MemoryManagerSlot::new(id).expect("usable slot"),
277 None,
278 SchemaMetadata::default(),
279 )
280 .expect("declaration")
281 }
282
283 #[test]
284 fn declaration_rejects_unbounded_label_metadata() {
285 let err = AllocationDeclaration::new(
286 "app.users.v1",
287 MemoryManagerSlot::new(100).expect("usable slot"),
288 Some("x".repeat(257)),
289 SchemaMetadata::default(),
290 )
291 .expect_err("label too long");
292
293 assert_eq!(err, DeclarationSnapshotError::LabelTooLong);
294 }
295
296 #[test]
297 fn memory_manager_declaration_constructor_builds_common_declaration() {
298 let declaration = AllocationDeclaration::memory_manager("app.orders.v1", 100, "orders")
299 .expect("declaration");
300
301 assert_eq!(declaration.stable_key.as_str(), "app.orders.v1");
302 assert_eq!(
303 declaration.slot,
304 MemoryManagerSlot::new(100).expect("usable slot")
305 );
306 assert_eq!(declaration.label.as_deref(), Some("orders"));
307 assert_eq!(declaration.schema, SchemaMetadata::default());
308 }
309
310 #[test]
311 fn memory_manager_declaration_constructor_rejects_invalid_slot() {
312 let err = AllocationDeclaration::memory_manager("app.orders.v1", u8::MAX, "orders")
313 .expect_err("sentinel must fail");
314
315 assert!(matches!(
316 err,
317 DeclarationSnapshotError::MemoryManagerSlot(_)
318 ));
319 }
320
321 #[test]
322 fn snapshot_decode_rejects_unusable_memory_manager_slot() {
323 let snapshot = DeclarationSnapshot::new(vec![declaration("app.orders.v1", 100)]).unwrap();
324 let mut value = serde_json::to_value(snapshot).unwrap();
325 value["declarations"][0]["slot"]["slot"]["MemoryManagerId"] = serde_json::json!(255);
326 assert!(serde_json::from_value::<DeclarationSnapshot>(value).is_err());
327 }
328
329 #[test]
330 fn rejects_duplicate_keys() {
331 let err = DeclarationSnapshot::new(vec![
332 declaration("app.users.v1", 100),
333 declaration("app.users.v1", 101),
334 ])
335 .expect_err("duplicate key");
336
337 assert_eq!(
338 err,
339 DeclarationSnapshotError::DuplicateStableKey(StableKey::parse("app.users.v1").unwrap())
340 );
341 }
342
343 #[test]
344 fn rejects_duplicate_slots() {
345 for second_key in ["app.orders.v1", "app.users.v1"] {
346 let err = DeclarationSnapshot::new(vec![
347 declaration("app.users.v1", 100),
348 declaration(second_key, 100),
349 ])
350 .expect_err("duplicate slot precedes duplicate key");
351
352 assert_eq!(
353 err,
354 DeclarationSnapshotError::DuplicateSlot(MemoryManagerSlot::new(100).unwrap())
355 );
356 }
357 }
358}