1use super::{AllocationRetirementError, LedgerIntegrityError};
2use crate::{
3 declaration::{AllocationDeclaration, DeclarationSnapshotError, validate_runtime_fingerprint},
4 key::StableKey,
5 schema::{SchemaMetadata, SchemaMetadataError},
6 slot::AllocationSlotDescriptor,
7 validation::Validate,
8};
9use serde::{Deserialize, Serialize};
10
11#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
28#[serde(deny_unknown_fields)]
29pub struct AllocationLedger {
30 pub(crate) current_generation: u64,
32 pub(crate) allocation_history: AllocationHistory,
34}
35
36#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
45#[serde(deny_unknown_fields)]
46pub struct AllocationHistory {
47 #[serde(deserialize_with = "crate::cbor::deserialize_records")]
49 pub(crate) records: Vec<AllocationRecord>,
50 #[serde(deserialize_with = "crate::cbor::deserialize_history")]
52 pub(crate) generations: Vec<GenerationRecord>,
53}
54
55#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
66#[serde(deny_unknown_fields)]
67pub struct AllocationRecord {
68 pub(crate) stable_key: StableKey,
70 pub(crate) slot: AllocationSlotDescriptor,
72 pub(crate) state: AllocationState,
74 pub(crate) first_generation: u64,
76 pub(crate) last_seen_generation: u64,
78 #[serde(deserialize_with = "crate::cbor::deserialize_history")]
80 pub(crate) schema_history: Vec<SchemaMetadataRecord>,
81}
82
83#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
91#[serde(deny_unknown_fields)]
92pub struct AllocationRetirement {
93 pub(crate) stable_key: StableKey,
95 pub(crate) slot: AllocationSlotDescriptor,
97}
98
99impl AllocationRetirement {
100 pub fn new(
102 stable_key: impl AsRef<str>,
103 slot: AllocationSlotDescriptor,
104 ) -> Result<Self, AllocationRetirementError> {
105 let retirement = Self {
106 stable_key: StableKey::parse(stable_key).map_err(AllocationRetirementError::Key)?,
107 slot,
108 };
109 retirement.validate()?;
110 Ok(retirement)
111 }
112
113 #[must_use]
115 pub const fn stable_key(&self) -> &StableKey {
116 &self.stable_key
117 }
118
119 #[must_use]
121 pub const fn slot(&self) -> &AllocationSlotDescriptor {
122 &self.slot
123 }
124
125 pub fn validate(&self) -> Result<(), AllocationRetirementError> {
127 self.stable_key
128 .validate()
129 .map_err(AllocationRetirementError::Key)?;
130 self.slot
131 .validate()
132 .map_err(AllocationRetirementError::MemoryManagerSlot)
133 }
134}
135
136#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
143#[serde(deny_unknown_fields)]
144pub enum AllocationState {
145 Reserved,
147 Active,
149 Retired {
151 generation: u64,
153 },
154}
155
156#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
165#[serde(deny_unknown_fields)]
166pub struct SchemaMetadataRecord {
167 pub(crate) generation: u64,
169 pub(crate) schema: SchemaMetadata,
171}
172
173#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
180#[serde(deny_unknown_fields)]
181pub struct GenerationRecord {
182 pub(crate) generation: u64,
184 pub(crate) parent_generation: u64,
186 #[serde(deserialize_with = "crate::cbor::deserialize_present_option")]
188 pub(crate) runtime_fingerprint: Option<String>,
189 pub(crate) declaration_count: u32,
191 #[serde(deserialize_with = "crate::cbor::deserialize_present_option")]
193 pub(crate) committed_at: Option<u64>,
194}
195
196#[derive(Clone, Debug, Eq, PartialEq)]
207pub struct RecoveredLedger {
208 ledger: AllocationLedger,
209 physical_generation: u64,
210}
211
212impl RecoveredLedger {
213 pub(crate) const fn from_trusted_parts(
214 ledger: AllocationLedger,
215 physical_generation: u64,
216 ) -> Self {
217 Self {
218 ledger,
219 physical_generation,
220 }
221 }
222
223 #[must_use]
229 pub const fn ledger(&self) -> &AllocationLedger {
230 &self.ledger
231 }
232
233 #[must_use]
235 pub const fn physical_generation(&self) -> u64 {
236 self.physical_generation
237 }
238
239 #[must_use]
241 pub const fn current_generation(&self) -> u64 {
242 self.ledger.current_generation
243 }
244
245 pub(crate) fn into_ledger(self) -> AllocationLedger {
246 self.ledger
247 }
248}
249
250impl AllocationHistory {
251 #[cfg(test)]
252 pub(crate) const fn from_parts(
253 records: Vec<AllocationRecord>,
254 generations: Vec<GenerationRecord>,
255 ) -> Self {
256 Self {
257 records,
258 generations,
259 }
260 }
261
262 #[must_use]
264 pub fn records(&self) -> &[AllocationRecord] {
265 &self.records
266 }
267
268 #[must_use]
270 pub fn generations(&self) -> &[GenerationRecord] {
271 &self.generations
272 }
273
274 #[must_use]
276 pub const fn is_empty(&self) -> bool {
277 self.records.is_empty() && self.generations.is_empty()
278 }
279
280 pub(crate) const fn records_mut(&mut self) -> &mut Vec<AllocationRecord> {
281 &mut self.records
282 }
283
284 #[cfg(test)]
285 pub(crate) const fn generations_mut(&mut self) -> &mut Vec<GenerationRecord> {
286 &mut self.generations
287 }
288
289 pub(crate) fn push_record(&mut self, record: AllocationRecord) {
290 self.records.push(record);
291 }
292
293 pub(crate) fn push_generation(&mut self, generation: GenerationRecord) {
294 self.generations.push(generation);
295 }
296}
297
298impl SchemaMetadataRecord {
299 pub fn new(generation: u64, schema: SchemaMetadata) -> Result<Self, SchemaMetadataError> {
301 schema.validate()?;
302 Ok(Self { generation, schema })
303 }
304
305 #[must_use]
307 pub const fn generation(&self) -> u64 {
308 self.generation
309 }
310
311 #[must_use]
313 pub const fn schema(&self) -> &SchemaMetadata {
314 &self.schema
315 }
316}
317
318impl GenerationRecord {
319 pub fn new(
321 generation: u64,
322 parent_generation: u64,
323 runtime_fingerprint: Option<String>,
324 declaration_count: u32,
325 committed_at: Option<u64>,
326 ) -> Result<Self, DeclarationSnapshotError> {
327 validate_runtime_fingerprint(runtime_fingerprint.as_deref())?;
328 Ok(Self {
329 generation,
330 parent_generation,
331 runtime_fingerprint,
332 declaration_count,
333 committed_at,
334 })
335 }
336
337 #[must_use]
339 pub const fn generation(&self) -> u64 {
340 self.generation
341 }
342
343 #[must_use]
345 pub const fn parent_generation(&self) -> u64 {
346 self.parent_generation
347 }
348
349 #[must_use]
351 pub fn runtime_fingerprint(&self) -> Option<&str> {
352 self.runtime_fingerprint.as_deref()
353 }
354
355 #[must_use]
357 pub const fn declaration_count(&self) -> u32 {
358 self.declaration_count
359 }
360
361 #[must_use]
363 pub const fn committed_at(&self) -> Option<u64> {
364 self.committed_at
365 }
366}
367
368impl AllocationRecord {
369 fn from_declaration(
370 generation: u64,
371 declaration: AllocationDeclaration,
372 state: AllocationState,
373 ) -> Result<Self, SchemaMetadataError> {
374 Ok(Self {
375 stable_key: declaration.stable_key,
376 slot: declaration.slot,
377 state,
378 first_generation: generation,
379 last_seen_generation: generation,
380 schema_history: vec![SchemaMetadataRecord::new(generation, declaration.schema)?],
381 })
382 }
383
384 pub(crate) fn active(
386 generation: u64,
387 declaration: AllocationDeclaration,
388 ) -> Result<Self, SchemaMetadataError> {
389 Self::from_declaration(generation, declaration, AllocationState::Active)
390 }
391
392 pub(crate) fn reserved(
394 generation: u64,
395 declaration: AllocationDeclaration,
396 ) -> Result<Self, SchemaMetadataError> {
397 Self::from_declaration(generation, declaration, AllocationState::Reserved)
398 }
399
400 #[must_use]
402 pub const fn stable_key(&self) -> &StableKey {
403 &self.stable_key
404 }
405
406 #[must_use]
408 pub const fn slot(&self) -> &AllocationSlotDescriptor {
409 &self.slot
410 }
411
412 #[must_use]
414 pub const fn state(&self) -> AllocationState {
415 self.state
416 }
417
418 #[must_use]
420 pub const fn first_generation(&self) -> u64 {
421 self.first_generation
422 }
423
424 #[must_use]
426 pub const fn last_seen_generation(&self) -> u64 {
427 self.last_seen_generation
428 }
429
430 #[must_use]
432 pub fn schema_history(&self) -> &[SchemaMetadataRecord] {
433 &self.schema_history
434 }
435
436 pub(crate) fn observe_declaration(
437 &mut self,
438 generation: u64,
439 declaration: &AllocationDeclaration,
440 ) -> Result<(), SchemaMetadataError> {
441 if self.state == AllocationState::Reserved {
442 self.state = AllocationState::Active;
443 }
444 self.observe_schema(generation, &declaration.schema)
445 }
446
447 pub(crate) fn observe_reservation(
448 &mut self,
449 generation: u64,
450 reservation: &AllocationDeclaration,
451 ) -> Result<(), SchemaMetadataError> {
452 self.observe_schema(generation, &reservation.schema)
453 }
454
455 fn observe_schema(
456 &mut self,
457 generation: u64,
458 schema: &SchemaMetadata,
459 ) -> Result<(), SchemaMetadataError> {
460 self.last_seen_generation = generation;
461
462 let latest_schema = self.schema_history.last().map(|record| &record.schema);
463 if latest_schema != Some(schema) {
464 self.schema_history
465 .push(SchemaMetadataRecord::new(generation, schema.clone())?);
466 }
467 Ok(())
468 }
469}
470
471impl AllocationLedger {
472 pub fn new(
480 current_generation: u64,
481 allocation_history: AllocationHistory,
482 ) -> Result<Self, LedgerIntegrityError> {
483 let ledger = Self {
484 current_generation,
485 allocation_history,
486 };
487 ledger.validate_integrity()?;
488 Ok(ledger)
489 }
490
491 pub fn new_committed(
497 current_generation: u64,
498 allocation_history: AllocationHistory,
499 ) -> Result<Self, LedgerIntegrityError> {
500 let ledger = Self {
501 current_generation,
502 allocation_history,
503 };
504 ledger.validate_committed_integrity()?;
505 Ok(ledger)
506 }
507
508 #[must_use]
510 pub const fn current_generation(&self) -> u64 {
511 self.current_generation
512 }
513
514 #[must_use]
516 pub const fn allocation_history(&self) -> &AllocationHistory {
517 &self.allocation_history
518 }
519}