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)]
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)]
206pub struct RecoveredLedger {
207 ledger: AllocationLedger,
208 physical_generation: u64,
209}
210
211impl RecoveredLedger {
212 pub(crate) const fn from_trusted_parts(
213 ledger: AllocationLedger,
214 physical_generation: u64,
215 ) -> Self {
216 Self {
217 ledger,
218 physical_generation,
219 }
220 }
221
222 #[must_use]
228 pub const fn ledger(&self) -> &AllocationLedger {
229 &self.ledger
230 }
231
232 #[must_use]
234 pub const fn physical_generation(&self) -> u64 {
235 self.physical_generation
236 }
237
238 #[must_use]
240 pub const fn current_generation(&self) -> u64 {
241 self.ledger.current_generation
242 }
243
244 pub(crate) fn into_ledger(self) -> AllocationLedger {
245 self.ledger
246 }
247}
248
249impl AllocationHistory {
250 #[cfg(test)]
251 pub(crate) const fn from_parts(
252 records: Vec<AllocationRecord>,
253 generations: Vec<GenerationRecord>,
254 ) -> Self {
255 Self {
256 records,
257 generations,
258 }
259 }
260
261 #[must_use]
263 pub fn records(&self) -> &[AllocationRecord] {
264 &self.records
265 }
266
267 #[must_use]
269 pub fn generations(&self) -> &[GenerationRecord] {
270 &self.generations
271 }
272
273 #[must_use]
275 pub const fn is_empty(&self) -> bool {
276 self.records.is_empty() && self.generations.is_empty()
277 }
278
279 pub(crate) const fn records_mut(&mut self) -> &mut Vec<AllocationRecord> {
280 &mut self.records
281 }
282
283 #[cfg(test)]
284 pub(crate) const fn generations_mut(&mut self) -> &mut Vec<GenerationRecord> {
285 &mut self.generations
286 }
287
288 pub(crate) fn push_record(&mut self, record: AllocationRecord) {
289 self.records.push(record);
290 }
291
292 pub(crate) fn push_generation(&mut self, generation: GenerationRecord) {
293 self.generations.push(generation);
294 }
295}
296
297impl SchemaMetadataRecord {
298 pub fn new(generation: u64, schema: SchemaMetadata) -> Result<Self, SchemaMetadataError> {
300 schema.validate()?;
301 Ok(Self { generation, schema })
302 }
303
304 #[must_use]
306 pub const fn generation(&self) -> u64 {
307 self.generation
308 }
309
310 #[must_use]
312 pub const fn schema(&self) -> &SchemaMetadata {
313 &self.schema
314 }
315}
316
317impl GenerationRecord {
318 pub fn new(
320 generation: u64,
321 parent_generation: u64,
322 runtime_fingerprint: Option<String>,
323 declaration_count: u32,
324 committed_at: Option<u64>,
325 ) -> Result<Self, DeclarationSnapshotError> {
326 validate_runtime_fingerprint(runtime_fingerprint.as_deref())?;
327 Ok(Self {
328 generation,
329 parent_generation,
330 runtime_fingerprint,
331 declaration_count,
332 committed_at,
333 })
334 }
335
336 #[must_use]
338 pub const fn generation(&self) -> u64 {
339 self.generation
340 }
341
342 #[must_use]
344 pub const fn parent_generation(&self) -> u64 {
345 self.parent_generation
346 }
347
348 #[must_use]
350 pub fn runtime_fingerprint(&self) -> Option<&str> {
351 self.runtime_fingerprint.as_deref()
352 }
353
354 #[must_use]
356 pub const fn declaration_count(&self) -> u32 {
357 self.declaration_count
358 }
359
360 #[must_use]
362 pub const fn committed_at(&self) -> Option<u64> {
363 self.committed_at
364 }
365}
366
367impl AllocationRecord {
368 fn from_declaration(
369 generation: u64,
370 declaration: AllocationDeclaration,
371 state: AllocationState,
372 ) -> Result<Self, SchemaMetadataError> {
373 Ok(Self {
374 stable_key: declaration.stable_key,
375 slot: declaration.slot,
376 state,
377 first_generation: generation,
378 last_seen_generation: generation,
379 schema_history: vec![SchemaMetadataRecord::new(generation, declaration.schema)?],
380 })
381 }
382
383 pub(crate) fn active(
385 generation: u64,
386 declaration: AllocationDeclaration,
387 ) -> Result<Self, SchemaMetadataError> {
388 Self::from_declaration(generation, declaration, AllocationState::Active)
389 }
390
391 pub(crate) fn reserved(
393 generation: u64,
394 declaration: AllocationDeclaration,
395 ) -> Result<Self, SchemaMetadataError> {
396 Self::from_declaration(generation, declaration, AllocationState::Reserved)
397 }
398
399 #[must_use]
401 pub const fn stable_key(&self) -> &StableKey {
402 &self.stable_key
403 }
404
405 #[must_use]
407 pub const fn slot(&self) -> &AllocationSlotDescriptor {
408 &self.slot
409 }
410
411 #[must_use]
413 pub const fn state(&self) -> AllocationState {
414 self.state
415 }
416
417 #[must_use]
419 pub const fn first_generation(&self) -> u64 {
420 self.first_generation
421 }
422
423 #[must_use]
425 pub const fn last_seen_generation(&self) -> u64 {
426 self.last_seen_generation
427 }
428
429 #[must_use]
431 pub fn schema_history(&self) -> &[SchemaMetadataRecord] {
432 &self.schema_history
433 }
434
435 pub(crate) fn observe_declaration(
436 &mut self,
437 generation: u64,
438 declaration: &AllocationDeclaration,
439 ) -> Result<(), SchemaMetadataError> {
440 self.last_seen_generation = generation;
441 if self.state == AllocationState::Reserved {
442 self.state = AllocationState::Active;
443 }
444
445 let latest_schema = self.schema_history.last().map(|record| &record.schema);
446 if latest_schema != Some(&declaration.schema) {
447 self.schema_history.push(SchemaMetadataRecord::new(
448 generation,
449 declaration.schema.clone(),
450 )?);
451 }
452 Ok(())
453 }
454
455 pub(crate) fn observe_reservation(
456 &mut self,
457 generation: u64,
458 reservation: &AllocationDeclaration,
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(&reservation.schema) {
464 self.schema_history.push(SchemaMetadataRecord::new(
465 generation,
466 reservation.schema.clone(),
467 )?);
468 }
469 Ok(())
470 }
471}
472
473impl AllocationLedger {
474 pub fn new(
482 current_generation: u64,
483 allocation_history: AllocationHistory,
484 ) -> Result<Self, LedgerIntegrityError> {
485 let ledger = Self {
486 current_generation,
487 allocation_history,
488 };
489 ledger.validate_integrity()?;
490 Ok(ledger)
491 }
492
493 pub fn new_committed(
499 current_generation: u64,
500 allocation_history: AllocationHistory,
501 ) -> Result<Self, LedgerIntegrityError> {
502 let ledger = Self::new(current_generation, allocation_history)?;
503 ledger.validate_committed_integrity()?;
504 Ok(ledger)
505 }
506
507 #[must_use]
509 pub const fn current_generation(&self) -> u64 {
510 self.current_generation
511 }
512
513 #[must_use]
515 pub const fn allocation_history(&self) -> &AllocationHistory {
516 &self.allocation_history
517 }
518}