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 retirement = Self {
105 stable_key: StableKey::parse(stable_key).map_err(AllocationRetirementError::Key)?,
106 slot,
107 };
108 retirement.validate()?;
109 Ok(retirement)
110 }
111
112 #[must_use]
114 pub const fn stable_key(&self) -> &StableKey {
115 &self.stable_key
116 }
117
118 #[must_use]
120 pub const fn slot(&self) -> &AllocationSlotDescriptor {
121 &self.slot
122 }
123
124 pub fn validate(&self) -> Result<(), AllocationRetirementError> {
126 self.stable_key
127 .validate()
128 .map_err(AllocationRetirementError::Key)?;
129 self.slot
130 .validate()
131 .map_err(AllocationRetirementError::MemoryManagerSlot)
132 }
133}
134
135#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
142#[serde(deny_unknown_fields)]
143pub enum AllocationState {
144 Reserved,
146 Active,
148 Retired {
150 generation: u64,
152 },
153}
154
155#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
164#[serde(deny_unknown_fields)]
165pub struct SchemaMetadataRecord {
166 pub(crate) generation: u64,
168 pub(crate) schema: SchemaMetadata,
170}
171
172#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
179#[serde(deny_unknown_fields)]
180pub struct GenerationRecord {
181 pub(crate) generation: u64,
183 pub(crate) parent_generation: u64,
185 #[serde(deserialize_with = "crate::cbor::deserialize_present_option")]
187 pub(crate) runtime_fingerprint: Option<String>,
188 pub(crate) declaration_count: u32,
190 #[serde(deserialize_with = "crate::cbor::deserialize_present_option")]
192 pub(crate) committed_at: Option<u64>,
193}
194
195#[derive(Clone, Debug, Eq, PartialEq)]
211pub struct RecoveredLedger {
212 ledger: AllocationLedger,
213}
214
215impl RecoveredLedger {
216 pub(crate) const fn from_trusted_ledger(ledger: AllocationLedger) -> Self {
217 Self { ledger }
218 }
219
220 #[must_use]
226 pub const fn ledger(&self) -> &AllocationLedger {
227 &self.ledger
228 }
229
230 #[must_use]
233 pub const fn physical_generation(&self) -> u64 {
234 self.ledger.current_generation
235 }
236
237 #[must_use]
239 pub const fn current_generation(&self) -> u64 {
240 self.ledger.current_generation
241 }
242
243 pub(crate) fn into_ledger(self) -> AllocationLedger {
244 self.ledger
245 }
246}
247
248impl AllocationHistory {
249 #[cfg(test)]
250 pub(crate) const fn from_parts(
251 records: Vec<AllocationRecord>,
252 generations: Vec<GenerationRecord>,
253 ) -> Self {
254 Self {
255 records,
256 generations,
257 }
258 }
259
260 #[must_use]
262 pub fn records(&self) -> &[AllocationRecord] {
263 &self.records
264 }
265
266 #[must_use]
268 pub fn generations(&self) -> &[GenerationRecord] {
269 &self.generations
270 }
271
272 #[must_use]
274 pub const fn is_empty(&self) -> bool {
275 self.records.is_empty() && self.generations.is_empty()
276 }
277
278 pub(crate) const fn records_mut(&mut self) -> &mut Vec<AllocationRecord> {
279 &mut self.records
280 }
281
282 #[cfg(test)]
283 pub(crate) const fn generations_mut(&mut self) -> &mut Vec<GenerationRecord> {
284 &mut self.generations
285 }
286
287 pub(crate) fn push_record(&mut self, record: AllocationRecord) {
288 self.records.push(record);
289 }
290
291 pub(crate) fn push_generation(&mut self, generation: GenerationRecord) {
292 self.generations.push(generation);
293 }
294}
295
296impl SchemaMetadataRecord {
297 pub fn new(generation: u64, schema: SchemaMetadata) -> Result<Self, SchemaMetadataError> {
299 schema.validate()?;
300 Ok(Self { generation, schema })
301 }
302
303 #[must_use]
305 pub const fn generation(&self) -> u64 {
306 self.generation
307 }
308
309 #[must_use]
311 pub const fn schema(&self) -> &SchemaMetadata {
312 &self.schema
313 }
314}
315
316impl GenerationRecord {
317 pub fn new(
319 generation: u64,
320 parent_generation: u64,
321 runtime_fingerprint: Option<String>,
322 declaration_count: u32,
323 committed_at: Option<u64>,
324 ) -> Result<Self, DeclarationSnapshotError> {
325 validate_runtime_fingerprint(runtime_fingerprint.as_deref())?;
326 Ok(Self {
327 generation,
328 parent_generation,
329 runtime_fingerprint,
330 declaration_count,
331 committed_at,
332 })
333 }
334
335 #[must_use]
337 pub const fn generation(&self) -> u64 {
338 self.generation
339 }
340
341 #[must_use]
343 pub const fn parent_generation(&self) -> u64 {
344 self.parent_generation
345 }
346
347 #[must_use]
349 pub fn runtime_fingerprint(&self) -> Option<&str> {
350 self.runtime_fingerprint.as_deref()
351 }
352
353 #[must_use]
355 pub const fn declaration_count(&self) -> u32 {
356 self.declaration_count
357 }
358
359 #[must_use]
361 pub const fn committed_at(&self) -> Option<u64> {
362 self.committed_at
363 }
364}
365
366impl AllocationRecord {
367 fn from_declaration(
370 generation: u64,
371 declaration: AllocationDeclaration,
372 state: AllocationState,
373 ) -> Self {
374 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 {
381 generation,
382 schema: declaration.schema,
383 }],
384 }
385 }
386
387 pub(crate) fn active(generation: u64, declaration: AllocationDeclaration) -> Self {
389 Self::from_declaration(generation, declaration, AllocationState::Active)
390 }
391
392 pub(crate) fn reserved(generation: u64, declaration: AllocationDeclaration) -> Self {
394 Self::from_declaration(generation, declaration, AllocationState::Reserved)
395 }
396
397 #[must_use]
399 pub const fn stable_key(&self) -> &StableKey {
400 &self.stable_key
401 }
402
403 #[must_use]
405 pub const fn slot(&self) -> &AllocationSlotDescriptor {
406 &self.slot
407 }
408
409 #[must_use]
411 pub const fn state(&self) -> AllocationState {
412 self.state
413 }
414
415 #[must_use]
417 pub const fn first_generation(&self) -> u64 {
418 self.first_generation
419 }
420
421 #[must_use]
423 pub const fn last_seen_generation(&self) -> u64 {
424 self.last_seen_generation
425 }
426
427 #[must_use]
429 pub fn schema_history(&self) -> &[SchemaMetadataRecord] {
430 &self.schema_history
431 }
432
433 pub(crate) fn observe_declaration(
434 &mut self,
435 generation: u64,
436 declaration: &AllocationDeclaration,
437 ) {
438 if self.state == AllocationState::Reserved {
439 self.state = AllocationState::Active;
440 }
441 self.observe_schema(generation, &declaration.schema);
442 }
443
444 pub(super) fn observe_schema(&mut self, generation: u64, schema: &SchemaMetadata) {
445 self.last_seen_generation = generation;
446
447 let latest_schema = self.schema_history.last().map(|record| &record.schema);
448 if latest_schema != Some(schema) {
449 self.schema_history.push(SchemaMetadataRecord {
450 generation,
451 schema: schema.clone(),
452 });
453 }
454 }
455}
456
457impl AllocationLedger {
458 pub(crate) fn empty_genesis() -> Self {
460 Self {
463 current_generation: 0,
464 allocation_history: AllocationHistory::default(),
465 }
466 }
467
468 pub fn new(
476 current_generation: u64,
477 allocation_history: AllocationHistory,
478 ) -> Result<Self, LedgerIntegrityError> {
479 let ledger = Self {
480 current_generation,
481 allocation_history,
482 };
483 ledger.validate_integrity()?;
484 Ok(ledger)
485 }
486
487 pub fn new_committed(
493 current_generation: u64,
494 allocation_history: AllocationHistory,
495 ) -> Result<Self, LedgerIntegrityError> {
496 let ledger = Self {
497 current_generation,
498 allocation_history,
499 };
500 ledger.validate_committed_integrity()?;
501 Ok(ledger)
502 }
503
504 #[must_use]
506 pub const fn current_generation(&self) -> u64 {
507 self.current_generation
508 }
509
510 #[must_use]
512 pub const fn allocation_history(&self) -> &AllocationHistory {
513 &self.allocation_history
514 }
515}