use crate::{
db::{DatabaseBootstrapError, MemoryBootstrapAdmissionError},
error::{Error, ErrorKind, ErrorOrigin, RuntimeErrorKind},
};
use ic_memory::{
AllocationValidationError, BootstrapAdmissionError, MemoryAllocationPoolError,
MemoryDeclarationError, MemoryResolutionError, RuntimeAdoptionError, RuntimeBootstrapError,
RuntimeConstructionError, RuntimeOpenError,
};
use icydb_diagnostic_code::{DiagnosticFactTag, RuntimeBoundaryCode};
pub(super) fn bootstrap_error(cause: &DatabaseBootstrapError) -> Error {
let classified = match cause {
DatabaseBootstrapError::Construction(cause) => construction_error(cause),
DatabaseBootstrapError::Binding(_) => None,
DatabaseBootstrapError::Bootstrap(cause) => cold_bootstrap_error(cause),
DatabaseBootstrapError::Adoption(cause) => adoption_error(cause),
};
classified.unwrap_or_else(|| {
Error::from_kind(
ErrorKind::Runtime(RuntimeErrorKind::Internal),
ErrorOrigin::Runtime,
)
})
}
fn construction_error(cause: &RuntimeConstructionError) -> Option<Error> {
match cause {
RuntimeConstructionError::BucketSizeMismatch {
persisted,
requested,
} => Some(boundary(
RuntimeBoundaryCode::MemoryBucketSizeMismatch,
vec![
(DiagnosticFactTag::Expected, u64::from(*requested)),
(DiagnosticFactTag::Actual, u64::from(*persisted)),
],
)),
_ => None,
}
}
fn cold_bootstrap_error(
cause: &RuntimeBootstrapError<MemoryBootstrapAdmissionError>,
) -> Option<Error> {
match cause {
RuntimeBootstrapError::DeclarationSnapshotMismatch
| RuntimeBootstrapError::AllocationPoolMismatch => Some(boundary(
RuntimeBoundaryCode::MemoryDeclarationSnapshotMismatch,
vec![],
)),
RuntimeBootstrapError::AdmissionPolicy(cause)
| RuntimeBootstrapError::Validation(AllocationValidationError::Policy(cause)) => {
admission_error(cause)
}
RuntimeBootstrapError::Admission(cause) => historical_error(cause),
RuntimeBootstrapError::Resolution(MemoryResolutionError::Exhausted { .. }) => {
Some(boundary(
RuntimeBoundaryCode::MemoryAllocationResolutionFailed,
vec![],
))
}
RuntimeBootstrapError::Resolution(MemoryResolutionError::Pool(cause)) => Some(boundary(
RuntimeBoundaryCode::MemoryAllocationResolutionFailed,
pool_id_facts(cause),
)),
RuntimeBootstrapError::Resolution(MemoryResolutionError::UnmanagedAllocation { id }) => {
Some(boundary(
RuntimeBoundaryCode::MemoryAllocationResolutionFailed,
vec![(DiagnosticFactTag::ActualMemoryId, u64::from(*id))],
))
}
RuntimeBootstrapError::Resolution(MemoryResolutionError::Declarations(cause)) => {
declaration_error(cause)
}
_ => None,
}
}
fn admission_error(cause: &MemoryBootstrapAdmissionError) -> Option<Error> {
match cause {
MemoryBootstrapAdmissionError::UnsupportedKey(_)
| MemoryBootstrapAdmissionError::InvalidDeclaration(_) => Some(boundary(
RuntimeBoundaryCode::MemoryDeclarationInvalid,
vec![],
)),
MemoryBootstrapAdmissionError::IncompleteRoles { store, .. } => Some(boundary(
RuntimeBoundaryCode::MemoryAllocationRolesIncomplete,
vec![(
DiagnosticFactTag::ExpectedCount,
if store.is_some() { 4 } else { 3 },
)],
)),
MemoryBootstrapAdmissionError::NamespaceRemoved(_) => Some(boundary(
RuntimeBoundaryCode::MemoryNamespaceRemoved,
vec![],
)),
MemoryBootstrapAdmissionError::HistoricalJournal(cause) => historical_error(cause),
}
}
fn historical_error(cause: &BootstrapAdmissionError) -> Option<Error> {
let facts = match cause {
BootstrapAdmissionError::Pool(cause) => pool_id_facts(cause),
BootstrapAdmissionError::Unknown(_)
| BootstrapAdmissionError::Duplicate(_)
| BootstrapAdmissionError::TooManyDeclarations => vec![],
BootstrapAdmissionError::Declarations(cause) => return declaration_error(cause),
_ => return None,
};
Some(boundary(
RuntimeBoundaryCode::MemoryHistoricalJournalUnavailable,
facts,
))
}
fn declaration_error(cause: &MemoryDeclarationError) -> Option<Error> {
match cause {
MemoryDeclarationError::TooManyDeclarations
| MemoryDeclarationError::DuplicateRequest { .. }
| MemoryDeclarationError::InvalidAuthority { .. }
| MemoryDeclarationError::ReservedAuthority { .. }
| MemoryDeclarationError::ReservedStableKey { .. } => Some(boundary(
RuntimeBoundaryCode::MemoryDeclarationInvalid,
vec![],
)),
_ => None,
}
}
fn adoption_error(cause: &RuntimeAdoptionError) -> Option<Error> {
let facts = match cause {
RuntimeAdoptionError::UnknownAuthority { .. }
| RuntimeAdoptionError::AuthorityMismatch { .. }
| RuntimeAdoptionError::Open(RuntimeOpenError::StableKeyNotCommitted(_)) => vec![],
_ => return None,
};
Some(boundary(
RuntimeBoundaryCode::MemoryDeclarationSnapshotMismatch,
facts,
))
}
fn pool_id_facts(cause: &MemoryAllocationPoolError) -> Vec<(DiagnosticFactTag, u64)> {
match cause {
MemoryAllocationPoolError::ExcludedSlot { id } => {
vec![(DiagnosticFactTag::ActualMemoryId, u64::from(*id))]
}
_ => vec![],
}
}
fn boundary(code: RuntimeBoundaryCode, facts: Vec<(DiagnosticFactTag, u64)>) -> Error {
Error::from_diagnostic_and_facts(
Error::from_runtime_boundary(code, ErrorOrigin::Runtime).diagnostic(),
facts,
)
}