1use super::{AllocationRetirementError, LedgerIntegrityError};
2use crate::{
3 declaration::{AllocationDeclaration, DeclarationSnapshotError, validate_runtime_fingerprint},
4 key::StableKey,
5 schema::{SchemaMetadata, SchemaMetadataError},
6 slot::AllocationSlotDescriptor,
7};
8use serde::{Deserialize, Serialize};
9
10#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
27#[serde(deny_unknown_fields)]
28pub struct AllocationLedger {
29 pub(crate) current_generation: u64,
31 pub(crate) allocation_history: AllocationHistory,
33}
34
35#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
44#[serde(deny_unknown_fields)]
45pub struct AllocationHistory {
46 #[serde(deserialize_with = "crate::cbor::deserialize_records")]
48 pub(crate) records: Vec<AllocationRecord>,
49 #[serde(deserialize_with = "crate::cbor::deserialize_history")]
51 pub(crate) generations: Vec<GenerationRecord>,
52}
53
54#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
65#[serde(deny_unknown_fields)]
66pub struct AllocationRecord {
67 pub(crate) stable_key: StableKey,
69 pub(crate) slot: AllocationSlotDescriptor,
71 pub(crate) state: AllocationState,
73 pub(crate) first_generation: u64,
75 pub(crate) last_seen_generation: u64,
77 #[serde(deserialize_with = "crate::cbor::deserialize_history")]
79 pub(crate) schema_history: Vec<SchemaMetadataRecord>,
80}
81
82#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
90#[serde(deny_unknown_fields)]
91pub struct AllocationRetirement {
92 pub(crate) stable_key: StableKey,
94 pub(crate) slot: AllocationSlotDescriptor,
96}
97
98impl AllocationRetirement {
99 pub fn new(
101 stable_key: impl AsRef<str>,
102 slot: AllocationSlotDescriptor,
103 ) -> Result<Self, AllocationRetirementError> {
104 let stable_key = StableKey::parse(stable_key).map_err(AllocationRetirementError::Key)?;
105 slot.validate()
106 .map_err(AllocationRetirementError::MemoryManagerSlot)?;
107 Ok(Self { stable_key, slot })
108 }
109
110 #[must_use]
112 pub const fn stable_key(&self) -> &StableKey {
113 &self.stable_key
114 }
115
116 #[must_use]
118 pub const fn slot(&self) -> &AllocationSlotDescriptor {
119 &self.slot
120 }
121
122 pub fn validate(&self) -> Result<(), AllocationRetirementError> {
124 self.stable_key
125 .validate()
126 .map_err(AllocationRetirementError::Key)?;
127 self.slot
128 .validate()
129 .map_err(AllocationRetirementError::MemoryManagerSlot)
130 }
131}
132
133#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
140#[serde(deny_unknown_fields)]
141pub enum AllocationState {
142 Reserved,
144 Active,
146 Retired {
148 generation: u64,
150 },
151}
152
153#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
162#[serde(deny_unknown_fields)]
163pub struct SchemaMetadataRecord {
164 pub(crate) generation: u64,
166 pub(crate) schema: SchemaMetadata,
168}
169
170#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
177#[serde(deny_unknown_fields)]
178pub struct GenerationRecord {
179 pub(crate) generation: u64,
181 pub(crate) parent_generation: u64,
183 #[serde(deserialize_with = "crate::cbor::deserialize_present_option")]
185 pub(crate) runtime_fingerprint: Option<String>,
186 pub(crate) declaration_count: u32,
188 #[serde(deserialize_with = "crate::cbor::deserialize_present_option")]
190 pub(crate) committed_at: Option<u64>,
191}
192
193#[derive(Clone, Debug, Eq, PartialEq)]
209pub struct RecoveredLedger {
210 ledger: AllocationLedger,
211}
212
213impl RecoveredLedger {
214 pub(crate) const fn from_trusted_ledger(ledger: AllocationLedger) -> Self {
215 Self { ledger }
216 }
217
218 #[must_use]
224 pub const fn ledger(&self) -> &AllocationLedger {
225 &self.ledger
226 }
227
228 #[must_use]
231 pub const fn physical_generation(&self) -> u64 {
232 self.ledger.current_generation
233 }
234
235 #[must_use]
237 pub const fn current_generation(&self) -> u64 {
238 self.ledger.current_generation
239 }
240
241 pub(crate) fn into_ledger(self) -> AllocationLedger {
242 self.ledger
243 }
244}
245
246impl AllocationHistory {
247 #[cfg(test)]
248 pub(crate) const fn from_parts(
249 records: Vec<AllocationRecord>,
250 generations: Vec<GenerationRecord>,
251 ) -> Self {
252 Self {
253 records,
254 generations,
255 }
256 }
257
258 #[must_use]
260 pub fn records(&self) -> &[AllocationRecord] {
261 &self.records
262 }
263
264 #[must_use]
266 pub fn generations(&self) -> &[GenerationRecord] {
267 &self.generations
268 }
269
270 #[must_use]
272 pub const fn is_empty(&self) -> bool {
273 self.records.is_empty() && self.generations.is_empty()
274 }
275
276 pub(crate) const fn records_mut(&mut self) -> &mut Vec<AllocationRecord> {
277 &mut self.records
278 }
279
280 #[cfg(test)]
281 pub(crate) const fn generations_mut(&mut self) -> &mut Vec<GenerationRecord> {
282 &mut self.generations
283 }
284
285 pub(crate) fn push_record(&mut self, record: AllocationRecord) {
286 self.records.push(record);
287 }
288
289 pub(crate) fn push_generation(&mut self, generation: GenerationRecord) {
290 self.generations.push(generation);
291 }
292}
293
294impl SchemaMetadataRecord {
295 pub fn new(generation: u64, schema: SchemaMetadata) -> Result<Self, SchemaMetadataError> {
297 schema.validate()?;
298 Ok(Self { generation, schema })
299 }
300
301 #[must_use]
303 pub const fn generation(&self) -> u64 {
304 self.generation
305 }
306
307 #[must_use]
309 pub const fn schema(&self) -> &SchemaMetadata {
310 &self.schema
311 }
312}
313
314impl GenerationRecord {
315 pub fn new(
317 generation: u64,
318 parent_generation: u64,
319 runtime_fingerprint: Option<String>,
320 declaration_count: u32,
321 committed_at: Option<u64>,
322 ) -> Result<Self, DeclarationSnapshotError> {
323 validate_runtime_fingerprint(runtime_fingerprint.as_deref())?;
324 Ok(Self {
325 generation,
326 parent_generation,
327 runtime_fingerprint,
328 declaration_count,
329 committed_at,
330 })
331 }
332
333 #[must_use]
335 pub const fn generation(&self) -> u64 {
336 self.generation
337 }
338
339 #[must_use]
341 pub const fn parent_generation(&self) -> u64 {
342 self.parent_generation
343 }
344
345 #[must_use]
347 pub fn runtime_fingerprint(&self) -> Option<&str> {
348 self.runtime_fingerprint.as_deref()
349 }
350
351 #[must_use]
353 pub const fn declaration_count(&self) -> u32 {
354 self.declaration_count
355 }
356
357 #[must_use]
359 pub const fn committed_at(&self) -> Option<u64> {
360 self.committed_at
361 }
362}
363
364impl AllocationRecord {
365 fn from_declaration(
368 generation: u64,
369 declaration: AllocationDeclaration,
370 state: AllocationState,
371 ) -> Self {
372 Self {
373 stable_key: declaration.stable_key,
374 slot: declaration.slot,
375 state,
376 first_generation: generation,
377 last_seen_generation: generation,
378 schema_history: vec![SchemaMetadataRecord {
379 generation,
380 schema: declaration.schema,
381 }],
382 }
383 }
384
385 pub(crate) fn active(generation: u64, declaration: AllocationDeclaration) -> Self {
387 Self::from_declaration(generation, declaration, AllocationState::Active)
388 }
389
390 pub(crate) fn reserved(generation: u64, declaration: AllocationDeclaration) -> Self {
392 Self::from_declaration(generation, declaration, AllocationState::Reserved)
393 }
394
395 #[must_use]
397 pub const fn stable_key(&self) -> &StableKey {
398 &self.stable_key
399 }
400
401 #[must_use]
403 pub const fn slot(&self) -> &AllocationSlotDescriptor {
404 &self.slot
405 }
406
407 #[must_use]
409 pub const fn state(&self) -> AllocationState {
410 self.state
411 }
412
413 #[must_use]
415 pub const fn first_generation(&self) -> u64 {
416 self.first_generation
417 }
418
419 #[must_use]
421 pub const fn last_seen_generation(&self) -> u64 {
422 self.last_seen_generation
423 }
424
425 #[must_use]
427 pub fn schema_history(&self) -> &[SchemaMetadataRecord] {
428 &self.schema_history
429 }
430
431 pub(crate) fn observe_declaration(
432 &mut self,
433 generation: u64,
434 declaration: &AllocationDeclaration,
435 ) {
436 if self.state == AllocationState::Reserved {
437 self.state = AllocationState::Active;
438 }
439 self.observe_schema(generation, &declaration.schema);
440 }
441
442 pub(super) fn observe_schema(&mut self, generation: u64, schema: &SchemaMetadata) {
443 self.last_seen_generation = generation;
444
445 let latest_schema = self.schema_history.last().map(|record| &record.schema);
446 if latest_schema != Some(schema) {
447 self.schema_history.push(SchemaMetadataRecord {
448 generation,
449 schema: schema.clone(),
450 });
451 }
452 }
453}
454
455impl AllocationLedger {
456 pub(crate) fn empty_genesis() -> Self {
458 Self {
461 current_generation: 0,
462 allocation_history: AllocationHistory::default(),
463 }
464 }
465
466 pub fn new(
474 current_generation: u64,
475 allocation_history: AllocationHistory,
476 ) -> Result<Self, LedgerIntegrityError> {
477 let ledger = Self {
478 current_generation,
479 allocation_history,
480 };
481 ledger.validate_integrity()?;
482 Ok(ledger)
483 }
484
485 pub fn new_committed(
491 current_generation: u64,
492 allocation_history: AllocationHistory,
493 ) -> Result<Self, LedgerIntegrityError> {
494 let ledger = Self {
495 current_generation,
496 allocation_history,
497 };
498 ledger.validate_committed_integrity()?;
499 Ok(ledger)
500 }
501
502 #[must_use]
504 pub const fn current_generation(&self) -> u64 {
505 self.current_generation
506 }
507
508 #[must_use]
510 pub const fn allocation_history(&self) -> &AllocationHistory {
511 &self.allocation_history
512 }
513}