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)]
62#[serde(deny_unknown_fields)]
63pub struct AllocationRecord {
64 pub(crate) stable_key: StableKey,
66 pub(crate) slot: AllocationSlotDescriptor,
68 pub(crate) state: AllocationState,
70 pub(crate) first_generation: u64,
72 pub(crate) last_seen_generation: u64,
74 pub(crate) retired_generation: Option<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)]
138pub enum AllocationState {
139 Reserved,
141 Active,
143 Retired,
145}
146
147#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
156#[serde(deny_unknown_fields)]
157pub struct SchemaMetadataRecord {
158 pub(crate) generation: u64,
160 pub(crate) schema: SchemaMetadata,
162}
163
164#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
169#[serde(deny_unknown_fields)]
170pub struct GenerationRecord {
171 pub(crate) generation: u64,
173 pub(crate) parent_generation: u64,
175 pub(crate) runtime_fingerprint: Option<String>,
177 pub(crate) declaration_count: u32,
179 pub(crate) committed_at: Option<u64>,
181}
182
183#[derive(Clone, Debug, Eq, PartialEq)]
194pub struct RecoveredLedger {
195 ledger: AllocationLedger,
196 physical_generation: u64,
197}
198
199impl RecoveredLedger {
200 pub(crate) const fn from_trusted_parts(
201 ledger: AllocationLedger,
202 physical_generation: u64,
203 ) -> Self {
204 Self {
205 ledger,
206 physical_generation,
207 }
208 }
209
210 #[must_use]
216 pub const fn ledger(&self) -> &AllocationLedger {
217 &self.ledger
218 }
219
220 #[must_use]
222 pub const fn physical_generation(&self) -> u64 {
223 self.physical_generation
224 }
225
226 #[must_use]
228 pub const fn current_generation(&self) -> u64 {
229 self.ledger.current_generation
230 }
231
232 pub(crate) fn into_ledger(self) -> AllocationLedger {
233 self.ledger
234 }
235}
236
237impl AllocationHistory {
238 #[cfg(test)]
239 pub(crate) const fn from_parts(
240 records: Vec<AllocationRecord>,
241 generations: Vec<GenerationRecord>,
242 ) -> Self {
243 Self {
244 records,
245 generations,
246 }
247 }
248
249 #[must_use]
251 pub fn records(&self) -> &[AllocationRecord] {
252 &self.records
253 }
254
255 #[must_use]
257 pub fn generations(&self) -> &[GenerationRecord] {
258 &self.generations
259 }
260
261 #[must_use]
263 pub fn is_empty(&self) -> bool {
264 self.records.is_empty() && self.generations.is_empty()
265 }
266
267 pub(crate) const fn records_mut(&mut self) -> &mut Vec<AllocationRecord> {
268 &mut self.records
269 }
270
271 #[cfg(test)]
272 pub(crate) const fn generations_mut(&mut self) -> &mut Vec<GenerationRecord> {
273 &mut self.generations
274 }
275
276 pub(crate) fn push_record(&mut self, record: AllocationRecord) {
277 self.records.push(record);
278 }
279
280 pub(crate) fn push_generation(&mut self, generation: GenerationRecord) {
281 self.generations.push(generation);
282 }
283}
284
285impl SchemaMetadataRecord {
286 pub fn new(generation: u64, schema: SchemaMetadata) -> Result<Self, SchemaMetadataError> {
288 schema.validate()?;
289 Ok(Self { generation, schema })
290 }
291
292 #[must_use]
294 pub const fn generation(&self) -> u64 {
295 self.generation
296 }
297
298 #[must_use]
300 pub const fn schema(&self) -> &SchemaMetadata {
301 &self.schema
302 }
303}
304
305impl GenerationRecord {
306 pub fn new(
308 generation: u64,
309 parent_generation: u64,
310 runtime_fingerprint: Option<String>,
311 declaration_count: u32,
312 committed_at: Option<u64>,
313 ) -> Result<Self, DeclarationSnapshotError> {
314 validate_runtime_fingerprint(runtime_fingerprint.as_deref())?;
315 Ok(Self {
316 generation,
317 parent_generation,
318 runtime_fingerprint,
319 declaration_count,
320 committed_at,
321 })
322 }
323
324 #[must_use]
326 pub const fn generation(&self) -> u64 {
327 self.generation
328 }
329
330 #[must_use]
332 pub const fn parent_generation(&self) -> u64 {
333 self.parent_generation
334 }
335
336 #[must_use]
338 pub fn runtime_fingerprint(&self) -> Option<&str> {
339 self.runtime_fingerprint.as_deref()
340 }
341
342 #[must_use]
344 pub const fn declaration_count(&self) -> u32 {
345 self.declaration_count
346 }
347
348 #[must_use]
350 pub const fn committed_at(&self) -> Option<u64> {
351 self.committed_at
352 }
353}
354
355impl AllocationRecord {
356 pub(crate) fn from_declaration(
358 generation: u64,
359 declaration: AllocationDeclaration,
360 state: AllocationState,
361 ) -> Result<Self, SchemaMetadataError> {
362 Ok(Self {
363 stable_key: declaration.stable_key,
364 slot: declaration.slot,
365 state,
366 first_generation: generation,
367 last_seen_generation: generation,
368 retired_generation: None,
369 schema_history: vec![SchemaMetadataRecord::new(generation, declaration.schema)?],
370 })
371 }
372
373 pub(crate) fn reserved(
375 generation: u64,
376 declaration: AllocationDeclaration,
377 ) -> Result<Self, SchemaMetadataError> {
378 Self::from_declaration(generation, declaration, AllocationState::Reserved)
379 }
380
381 #[must_use]
383 pub const fn stable_key(&self) -> &StableKey {
384 &self.stable_key
385 }
386
387 #[must_use]
389 pub const fn slot(&self) -> &AllocationSlotDescriptor {
390 &self.slot
391 }
392
393 #[must_use]
395 pub const fn state(&self) -> AllocationState {
396 self.state
397 }
398
399 #[must_use]
401 pub const fn first_generation(&self) -> u64 {
402 self.first_generation
403 }
404
405 #[must_use]
407 pub const fn last_seen_generation(&self) -> u64 {
408 self.last_seen_generation
409 }
410
411 #[must_use]
413 pub const fn retired_generation(&self) -> Option<u64> {
414 self.retired_generation
415 }
416
417 #[must_use]
419 pub fn schema_history(&self) -> &[SchemaMetadataRecord] {
420 &self.schema_history
421 }
422
423 pub(crate) fn observe_declaration(
424 &mut self,
425 generation: u64,
426 declaration: &AllocationDeclaration,
427 ) -> Result<(), SchemaMetadataError> {
428 self.last_seen_generation = generation;
429 if self.state == AllocationState::Reserved {
430 self.state = AllocationState::Active;
431 }
432
433 let latest_schema = self.schema_history.last().map(|record| &record.schema);
434 if latest_schema != Some(&declaration.schema) {
435 self.schema_history.push(SchemaMetadataRecord::new(
436 generation,
437 declaration.schema.clone(),
438 )?);
439 }
440 Ok(())
441 }
442
443 pub(crate) fn observe_reservation(
444 &mut self,
445 generation: u64,
446 reservation: &AllocationDeclaration,
447 ) -> Result<(), SchemaMetadataError> {
448 self.last_seen_generation = generation;
449
450 let latest_schema = self.schema_history.last().map(|record| &record.schema);
451 if latest_schema != Some(&reservation.schema) {
452 self.schema_history.push(SchemaMetadataRecord::new(
453 generation,
454 reservation.schema.clone(),
455 )?);
456 }
457 Ok(())
458 }
459}
460
461impl AllocationLedger {
462 pub fn new(
470 current_generation: u64,
471 allocation_history: AllocationHistory,
472 ) -> Result<Self, LedgerIntegrityError> {
473 let ledger = Self {
474 current_generation,
475 allocation_history,
476 };
477 ledger.validate_integrity()?;
478 Ok(ledger)
479 }
480
481 pub fn new_committed(
487 current_generation: u64,
488 allocation_history: AllocationHistory,
489 ) -> Result<Self, LedgerIntegrityError> {
490 let ledger = Self::new(current_generation, allocation_history)?;
491 ledger.validate_committed_integrity()?;
492 Ok(ledger)
493 }
494
495 #[must_use]
497 pub const fn current_generation(&self) -> u64 {
498 self.current_generation
499 }
500
501 #[must_use]
503 pub const fn allocation_history(&self) -> &AllocationHistory {
504 &self.allocation_history
505 }
506}