use core::error::Error;
use crate::allocation::AllocationError;
use crate::bytes::{
NonAsciiInputByte, PayloadByteCount, ReturnOutputByteCount, RuntimeInputByteCount,
RuntimeStateByteCount,
};
use crate::limits::{
ReturnByteLimit, RuntimeInputByteLimit, RuntimeStateByteLimit, StepCount, StepLimit,
};
#[derive(Debug, PartialEq, Eq)]
pub enum RunError {
Allocation(AllocationError),
InternalInvariant(RunInvariantError),
StateSize(StateSizeError),
Limit(LimitError),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RunInvariantError {
MissingOnceRuleState {
rule: crate::inspect::RulePosition,
available_slots: crate::inspect::OnceRuleCount,
},
InvalidStateMatchRange {
matched_state_len: RuntimeStateByteCount,
current_state_len: RuntimeStateByteCount,
},
}
impl Error for RunInvariantError {}
impl Error for RunError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Allocation(error) => Some(error),
Self::InternalInvariant(error) => Some(error),
Self::StateSize(error) => Some(error),
Self::Limit(error) => Some(error),
}
}
}
impl From<AllocationError> for RunError {
fn from(value: AllocationError) -> Self {
Self::Allocation(value)
}
}
impl From<RunInvariantError> for RunError {
fn from(value: RunInvariantError) -> Self {
Self::InternalInvariant(value)
}
}
impl From<StateSizeError> for RunError {
fn from(value: StateSizeError) -> Self {
Self::StateSize(value)
}
}
impl From<LimitError> for RunError {
fn from(value: LimitError) -> Self {
Self::Limit(value)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RuntimeInputError {
NonAscii {
column: InputColumn,
byte: NonAsciiInputByte,
},
ColumnOverflow,
InputLimit {
limit: RuntimeInputByteLimit,
attempted_len: RuntimeInputByteCount,
},
InternalInvariant(RuntimeInputInvariantError),
Allocation(AllocationError),
}
impl RuntimeInputError {
pub(crate) const fn non_ascii(column: InputColumn, byte: NonAsciiInputByte) -> Self {
Self::NonAscii { column, byte }
}
pub(crate) const fn column_overflow() -> Self {
Self::ColumnOverflow
}
pub(crate) const fn input_limit(
limit: RuntimeInputByteLimit,
attempted_len: RuntimeInputByteCount,
) -> Self {
Self::InputLimit {
limit,
attempted_len,
}
}
pub(crate) const fn internal_invariant(error: RuntimeInputInvariantError) -> Self {
Self::InternalInvariant(error)
}
}
impl Error for RuntimeInputError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Allocation(error) => Some(error),
Self::InternalInvariant(error) => Some(error),
Self::NonAscii { .. } | Self::ColumnOverflow | Self::InputLimit { .. } => None,
}
}
}
impl From<AllocationError> for RuntimeInputError {
fn from(value: AllocationError) -> Self {
Self::Allocation(value)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RuntimeInputInvariantError {
MissingValidatedAsciiByte,
}
impl Error for RuntimeInputInvariantError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RunAdmissionError {
InitialStateTooLarge {
limit: RuntimeStateByteLimit,
attempted_len: RuntimeStateByteCount,
},
}
impl RunAdmissionError {
pub(crate) const fn initial_state_limit(
limit: RuntimeStateByteLimit,
attempted_len: RuntimeStateByteCount,
) -> Self {
Self::InitialStateTooLarge {
limit,
attempted_len,
}
}
}
impl Error for RunAdmissionError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct InputColumn {
one_based: usize,
}
impl InputColumn {
pub(crate) fn from_zero_based(zero_based: usize) -> Option<Self> {
let one_based = zero_based.checked_add(1)?;
Some(Self { one_based })
}
#[must_use]
pub const fn get(self) -> usize {
self.one_based
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StateSizeError {
state: RuntimeStateByteCount,
lhs: PayloadByteCount,
rhs: PayloadByteCount,
}
impl StateSizeError {
pub(crate) const fn new(
state_len: RuntimeStateByteCount,
lhs_len: PayloadByteCount,
rhs_len: PayloadByteCount,
) -> Self {
Self {
state: state_len,
lhs: lhs_len,
rhs: rhs_len,
}
}
#[must_use]
pub const fn state_len(&self) -> RuntimeStateByteCount {
self.state
}
#[must_use]
pub const fn lhs_len(&self) -> PayloadByteCount {
self.lhs
}
#[must_use]
pub const fn rhs_len(&self) -> PayloadByteCount {
self.rhs
}
}
impl Error for StateSizeError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LimitError {
State {
limit: RuntimeStateByteLimit,
attempted_len: RuntimeStateByteCount,
},
Return {
limit: ReturnByteLimit,
attempted_len: ReturnOutputByteCount,
},
Step {
max_steps: StepLimit,
completed_steps: StepCount,
state_len: RuntimeStateByteCount,
},
}
impl LimitError {
pub(crate) const fn state(
limit: RuntimeStateByteLimit,
attempted_len: RuntimeStateByteCount,
) -> Self {
Self::State {
limit,
attempted_len,
}
}
pub(crate) const fn return_output(
limit: ReturnByteLimit,
attempted_len: ReturnOutputByteCount,
) -> Self {
Self::Return {
limit,
attempted_len,
}
}
pub(crate) const fn step(
max_steps: StepLimit,
completed_steps: StepCount,
state_len: RuntimeStateByteCount,
) -> Self {
Self::Step {
max_steps,
completed_steps,
state_len,
}
}
}
impl Error for LimitError {}
#[cfg(test)]
mod tests {
use super::InputColumn;
use crate::test_support::{TestResult, ensure_eq};
#[test]
fn input_column_rejects_unrepresentable_zero_based_index() -> TestResult {
ensure_eq!(InputColumn::from_zero_based(usize::MAX), None)?;
ensure_eq!(
InputColumn::from_zero_based(0).map(InputColumn::get),
Some(1),
)?;
Ok(())
}
}