1use crate::{
2 key::{StableKey, StableKeyError},
3 schema::{SchemaMetadata, SchemaMetadataError},
4 slot::AllocationSlotDescriptor,
5};
6use serde::{Deserialize, Serialize};
7use std::collections::BTreeSet;
8
9#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
14pub struct AllocationDeclaration {
15 pub stable_key: StableKey,
17 pub slot: AllocationSlotDescriptor,
19 pub label: Option<String>,
21 pub schema: SchemaMetadata,
23}
24
25impl AllocationDeclaration {
26 pub fn new(
28 stable_key: impl AsRef<str>,
29 slot: AllocationSlotDescriptor,
30 label: Option<String>,
31 schema: SchemaMetadata,
32 ) -> Result<Self, DeclarationSnapshotError> {
33 let stable_key = StableKey::parse(stable_key).map_err(DeclarationSnapshotError::Key)?;
34 schema
35 .validate()
36 .map_err(DeclarationSnapshotError::SchemaMetadata)?;
37 Ok(Self {
38 stable_key,
39 slot,
40 label,
41 schema,
42 })
43 }
44}
45
46#[derive(Clone, Debug, Default)]
51pub struct DeclarationCollector {
52 declarations: Vec<AllocationDeclaration>,
53}
54
55impl DeclarationCollector {
56 pub fn push(&mut self, declaration: AllocationDeclaration) {
58 self.declarations.push(declaration);
59 }
60
61 pub fn seal(self) -> Result<DeclarationSnapshot, DeclarationSnapshotError> {
63 DeclarationSnapshot::new(self.declarations)
64 }
65}
66
67#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
72pub struct DeclarationSnapshot {
73 declarations: Vec<AllocationDeclaration>,
75 runtime_fingerprint: Option<String>,
77}
78
79impl DeclarationSnapshot {
80 pub fn new(declarations: Vec<AllocationDeclaration>) -> Result<Self, DeclarationSnapshotError> {
82 reject_duplicates(&declarations)?;
83 Ok(Self {
84 declarations,
85 runtime_fingerprint: None,
86 })
87 }
88
89 #[must_use]
91 pub fn with_runtime_fingerprint(mut self, fingerprint: impl Into<String>) -> Self {
92 self.runtime_fingerprint = Some(fingerprint.into());
93 self
94 }
95
96 #[must_use]
98 pub const fn is_empty(&self) -> bool {
99 self.declarations.is_empty()
100 }
101
102 #[must_use]
104 pub const fn len(&self) -> usize {
105 self.declarations.len()
106 }
107
108 #[must_use]
110 pub fn declarations(&self) -> &[AllocationDeclaration] {
111 &self.declarations
112 }
113
114 #[must_use]
116 pub fn runtime_fingerprint(&self) -> Option<&str> {
117 self.runtime_fingerprint.as_deref()
118 }
119
120 pub(crate) fn into_parts(self) -> (Vec<AllocationDeclaration>, Option<String>) {
121 (self.declarations, self.runtime_fingerprint)
122 }
123}
124
125#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
130pub enum DeclarationSnapshotError {
131 #[error(transparent)]
133 Key(StableKeyError),
134 #[error(transparent)]
136 SchemaMetadata(SchemaMetadataError),
137 #[error("stable key '{0}' is declared more than once")]
139 DuplicateStableKey(StableKey),
140 #[error("allocation slot '{0:?}' is declared more than once")]
142 DuplicateSlot(AllocationSlotDescriptor),
143}
144
145fn reject_duplicates(
146 declarations: &[AllocationDeclaration],
147) -> Result<(), DeclarationSnapshotError> {
148 let mut keys = BTreeSet::new();
149 let mut slots = BTreeSet::new();
150
151 for declaration in declarations {
152 if !slots.insert(declaration.slot.clone()) {
153 return Err(DeclarationSnapshotError::DuplicateSlot(
154 declaration.slot.clone(),
155 ));
156 }
157 if !keys.insert(declaration.stable_key.clone()) {
158 return Err(DeclarationSnapshotError::DuplicateStableKey(
159 declaration.stable_key.clone(),
160 ));
161 }
162 }
163
164 Ok(())
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 use crate::slot::AllocationSlotDescriptor;
171
172 fn declaration(key: &str, id: u8) -> AllocationDeclaration {
173 AllocationDeclaration::new(
174 key,
175 AllocationSlotDescriptor::memory_manager(id),
176 None,
177 SchemaMetadata::default(),
178 )
179 .expect("declaration")
180 }
181
182 #[test]
183 fn rejects_duplicate_keys() {
184 let err = DeclarationSnapshot::new(vec![
185 declaration("app.users.v1", 100),
186 declaration("app.users.v1", 101),
187 ])
188 .expect_err("duplicate key");
189
190 assert!(matches!(
191 err,
192 DeclarationSnapshotError::DuplicateStableKey(_)
193 ));
194 }
195
196 #[test]
197 fn rejects_duplicate_slots() {
198 let err = DeclarationSnapshot::new(vec![
199 declaration("app.users.v1", 100),
200 declaration("app.orders.v1", 100),
201 ])
202 .expect_err("duplicate slot");
203
204 assert!(matches!(err, DeclarationSnapshotError::DuplicateSlot(_)));
205 }
206}