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 pub(crate) records: Vec<AllocationRecord>,
49 pub(crate) generations: Vec<GenerationRecord>,
51}
52
53#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
64#[serde(deny_unknown_fields)]
65pub struct AllocationRecord {
66 pub(crate) stable_key: StableKey,
68 pub(crate) slot: AllocationSlotDescriptor,
70 pub(crate) state: AllocationState,
72 pub(crate) first_generation: u64,
74 pub(crate) last_seen_generation: u64,
76 pub(crate) schema_history: Vec<SchemaMetadataRecord>,
78}
79
80#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
88#[serde(deny_unknown_fields)]
89pub struct AllocationRetirement {
90 pub(crate) stable_key: StableKey,
92 pub(crate) slot: AllocationSlotDescriptor,
94}
95
96impl AllocationRetirement {
97 pub fn new(
99 stable_key: impl AsRef<str>,
100 slot: AllocationSlotDescriptor,
101 ) -> Result<Self, AllocationRetirementError> {
102 let retirement = Self {
103 stable_key: StableKey::parse(stable_key).map_err(AllocationRetirementError::Key)?,
104 slot,
105 };
106 retirement.validate()?;
107 Ok(retirement)
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)]
140pub enum AllocationState {
141 Reserved,
143 Active,
145 Retired {
147 generation: u64,
149 },
150}
151
152#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
161#[serde(deny_unknown_fields)]
162pub struct SchemaMetadataRecord {
163 pub(crate) generation: u64,
165 pub(crate) schema: SchemaMetadata,
167}
168
169#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
176#[serde(deny_unknown_fields)]
177pub struct GenerationRecord {
178 pub(crate) generation: u64,
180 pub(crate) parent_generation: u64,
182 #[serde(deserialize_with = "crate::cbor::deserialize_present_option")]
184 pub(crate) runtime_fingerprint: Option<String>,
185 pub(crate) declaration_count: u32,
187 #[serde(deserialize_with = "crate::cbor::deserialize_present_option")]
189 pub(crate) committed_at: Option<u64>,
190}
191
192#[derive(Clone, Debug, Eq, PartialEq)]
203pub struct RecoveredLedger {
204 ledger: AllocationLedger,
205 physical_generation: u64,
206}
207
208impl RecoveredLedger {
209 pub(crate) const fn from_trusted_parts(
210 ledger: AllocationLedger,
211 physical_generation: u64,
212 ) -> Self {
213 Self {
214 ledger,
215 physical_generation,
216 }
217 }
218
219 #[must_use]
225 pub const fn ledger(&self) -> &AllocationLedger {
226 &self.ledger
227 }
228
229 #[must_use]
231 pub const fn physical_generation(&self) -> u64 {
232 self.physical_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 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(
366 generation: u64,
367 declaration: AllocationDeclaration,
368 state: AllocationState,
369 ) -> Result<Self, SchemaMetadataError> {
370 Ok(Self {
371 stable_key: declaration.stable_key,
372 slot: declaration.slot,
373 state,
374 first_generation: generation,
375 last_seen_generation: generation,
376 schema_history: vec![SchemaMetadataRecord::new(generation, declaration.schema)?],
377 })
378 }
379
380 pub(crate) fn active(
382 generation: u64,
383 declaration: AllocationDeclaration,
384 ) -> Result<Self, SchemaMetadataError> {
385 Self::from_declaration(generation, declaration, AllocationState::Active)
386 }
387
388 pub(crate) fn reserved(
390 generation: u64,
391 declaration: AllocationDeclaration,
392 ) -> Result<Self, SchemaMetadataError> {
393 Self::from_declaration(generation, declaration, AllocationState::Reserved)
394 }
395
396 #[must_use]
398 pub const fn stable_key(&self) -> &StableKey {
399 &self.stable_key
400 }
401
402 #[must_use]
404 pub const fn slot(&self) -> &AllocationSlotDescriptor {
405 &self.slot
406 }
407
408 #[must_use]
410 pub const fn state(&self) -> AllocationState {
411 self.state
412 }
413
414 #[must_use]
416 pub const fn first_generation(&self) -> u64 {
417 self.first_generation
418 }
419
420 #[must_use]
422 pub const fn last_seen_generation(&self) -> u64 {
423 self.last_seen_generation
424 }
425
426 #[must_use]
428 pub fn schema_history(&self) -> &[SchemaMetadataRecord] {
429 &self.schema_history
430 }
431
432 pub(crate) fn observe_declaration(
433 &mut self,
434 generation: u64,
435 declaration: &AllocationDeclaration,
436 ) -> Result<(), SchemaMetadataError> {
437 self.last_seen_generation = generation;
438 if self.state == AllocationState::Reserved {
439 self.state = AllocationState::Active;
440 }
441
442 let latest_schema = self.schema_history.last().map(|record| &record.schema);
443 if latest_schema != Some(&declaration.schema) {
444 self.schema_history.push(SchemaMetadataRecord::new(
445 generation,
446 declaration.schema.clone(),
447 )?);
448 }
449 Ok(())
450 }
451
452 pub(crate) fn observe_reservation(
453 &mut self,
454 generation: u64,
455 reservation: &AllocationDeclaration,
456 ) -> Result<(), SchemaMetadataError> {
457 self.last_seen_generation = generation;
458
459 let latest_schema = self.schema_history.last().map(|record| &record.schema);
460 if latest_schema != Some(&reservation.schema) {
461 self.schema_history.push(SchemaMetadataRecord::new(
462 generation,
463 reservation.schema.clone(),
464 )?);
465 }
466 Ok(())
467 }
468}
469
470impl AllocationLedger {
471 pub fn new(
479 current_generation: u64,
480 allocation_history: AllocationHistory,
481 ) -> Result<Self, LedgerIntegrityError> {
482 let ledger = Self {
483 current_generation,
484 allocation_history,
485 };
486 ledger.validate_integrity()?;
487 Ok(ledger)
488 }
489
490 pub fn new_committed(
496 current_generation: u64,
497 allocation_history: AllocationHistory,
498 ) -> Result<Self, LedgerIntegrityError> {
499 let ledger = Self::new(current_generation, allocation_history)?;
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}