use crate::{
AllocationLedger, AllocationSlotDescriptor, AllocationState, MemoryRequest, SchemaMetadata,
SealedDeclarationSnapshot, StableKey,
};
#[derive(Clone, Copy, Debug)]
pub struct RecoveredAllocationMetadata<'a> {
pub stable_key: &'a StableKey,
pub slot: &'a AllocationSlotDescriptor,
pub state: AllocationState,
pub schema: &'a SchemaMetadata,
}
#[non_exhaustive]
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
pub enum BootstrapAdmissionError {
#[error("historical key {0} is unknown")]
Unknown(StableKey),
#[error("historical key {0} is retired")]
Retired(StableKey),
#[error("key {0} is already declared or selected")]
Duplicate(StableKey),
#[error("completed declarations exceed 254 external allocations")]
TooManyDeclarations,
#[error("historical selection {stable_key} by {authority} lacks a current grant: {source}")]
Range {
stable_key: StableKey,
authority: String,
source: crate::MemoryManagerRangeAuthorityError,
},
#[error(transparent)]
Registry(#[from] crate::StaticMemoryDeclarationError),
}
pub struct BootstrapAdmission<'a> {
ledger: &'a AllocationLedger,
declarations: &'a SealedDeclarationSnapshot,
selected: Vec<MemoryRequest>,
failure: Option<BootstrapAdmissionError>,
}
impl<'a> BootstrapAdmission<'a> {
pub(super) const fn new(
ledger: &'a AllocationLedger,
declarations: &'a SealedDeclarationSnapshot,
) -> Self {
Self {
ledger,
declarations,
selected: Vec::new(),
failure: None,
}
}
#[must_use]
pub const fn declarations(&self) -> &SealedDeclarationSnapshot {
self.declarations
}
#[must_use = "recovered allocation metadata is inspected only when the iterator is consumed"]
pub fn recovered_allocations(
&self,
) -> impl ExactSizeIterator<Item = RecoveredAllocationMetadata<'_>> {
self.ledger
.allocation_history()
.records()
.iter()
.map(|record| RecoveredAllocationMetadata {
stable_key: record.stable_key(),
slot: record.slot(),
state: record.state(),
schema: record
.schema_history()
.last()
.expect("validated schema history")
.schema(),
})
}
#[must_use]
pub fn is_declared(&self, key: &StableKey) -> bool {
self.declarations
.allocation_snapshot()
.declarations()
.iter()
.any(|d| d.stable_key() == key)
|| self
.declarations
.requests()
.iter()
.chain(&self.selected)
.any(|r| r.stable_key() == key)
}
pub fn include_historical(
&mut self,
authority: &str,
stable_key: &str,
) -> Result<(), BootstrapAdmissionError> {
if let Some(error) = &self.failure {
return Err(error.clone());
}
let result = self.select(authority, stable_key);
if let Err(error) = &result {
self.failure = Some(error.clone());
}
result
}
fn select(&mut self, authority: &str, stable_key: &str) -> Result<(), BootstrapAdmissionError> {
let request = MemoryRequest::new(authority, stable_key, SchemaMetadata::default())?;
let key = request.stable_key();
if self.is_declared(key) {
return Err(BootstrapAdmissionError::Duplicate(key.clone()));
}
if self.declarations.registered_declarations().len()
+ self.declarations.requests().len()
+ self.selected.len()
>= 254
{
return Err(BootstrapAdmissionError::TooManyDeclarations);
}
let record = self
.ledger
.allocation_history()
.records()
.iter()
.find(|r| r.stable_key() == key)
.ok_or_else(|| BootstrapAdmissionError::Unknown(key.clone()))?;
if matches!(record.state(), AllocationState::Retired { .. }) {
return Err(BootstrapAdmissionError::Retired(key.clone()));
}
self.declarations
.range_authority()
.validate_slot_authority(record.slot(), authority)
.map_err(|source| BootstrapAdmissionError::Range {
stable_key: key.clone(),
authority: authority.to_string(),
source,
})?;
self.selected.push(
request.with_schema(
record
.schema_history()
.last()
.expect("validated schema history")
.schema()
.clone(),
),
);
Ok(())
}
pub(super) fn complete(self) -> Result<Vec<MemoryRequest>, BootstrapAdmissionError> {
if let Some(error) = self.failure {
return Err(error);
}
Ok(self.selected)
}
}