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