use core::error::Error;
use crate::allocation::AllocationError;
use crate::bytes::{
NonAsciiInputByte, PayloadByteCount, ReturnOutputByteCount, RuntimeInputByteCount,
RuntimeStateByteCount,
};
use crate::program::{
ReturnByteLimit, RuntimeInputByteLimit, RuntimeStateByteLimit, StepCount, StepLimit,
};
#[derive(Debug, PartialEq, Eq)]
pub enum RunError {
Allocation(AllocationError),
StateSize(StateSizeError),
Limit(LimitError),
}
impl Error for RunError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Allocation(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<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,
Limit {
limit: RuntimeInputByteLimit,
attempted_len: RuntimeInputByteCount,
},
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 limit(
limit: RuntimeInputByteLimit,
attempted_len: RuntimeInputByteCount,
) -> Self {
Self::Limit {
limit,
attempted_len,
}
}
}
impl Error for RuntimeInputError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Allocation(error) => Some(error),
Self::NonAscii { .. } | Self::ColumnOverflow | Self::Limit { .. } => None,
}
}
}
impl From<AllocationError> for RuntimeInputError {
fn from(value: AllocationError) -> Self {
Self::Allocation(value)
}
}
#[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, Copy, PartialEq, Eq)]
pub enum StateLimitContext {
Input,
Rewrite,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LimitError {
State {
context: StateLimitContext,
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(
context: StateLimitContext,
limit: RuntimeStateByteLimit,
attempted_len: RuntimeStateByteCount,
) -> Self {
Self::State {
context,
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(())
}
}