use crate::bytes::{ReturnOutputByteCount, RuntimeStateByteCount};
use crate::error::{
ReturnOutputLimitError, RuleAttemptLimitError, RuleAttemptStepError, RunStepError,
RuntimeStateLimitError, StepLimitError,
};
use crate::limits::{ExecutionLimits, RuleAttemptCount, RuleAttemptLimit, StepCount};
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct RuntimeBudgetState {
limits: ExecutionLimits,
completed_steps: StepCount,
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct RuleAttemptBudgetState {
limit: RuleAttemptLimit,
completed_attempts: RuleAttemptCount,
}
#[derive(Debug)]
pub(crate) struct StepReservation<'budget> {
budget: &'budget mut RuntimeBudgetState,
next_step: StepCount,
}
#[derive(Debug)]
pub(crate) struct RuleAttemptReservation<'budget> {
budget: &'budget mut RuleAttemptBudgetState,
next_attempt: RuleAttemptCount,
}
impl RuntimeBudgetState {
pub(crate) const fn new(limits: ExecutionLimits) -> Self {
Self {
limits,
completed_steps: StepCount::ZERO,
}
}
pub(crate) const fn completed_steps(&self) -> StepCount {
self.completed_steps
}
pub(crate) fn ensure_rewrite_state_len(
&self,
attempted_len: RuntimeStateByteCount,
) -> Result<(), RunStepError> {
let limit = self.limits.state_byte_limit();
if limit.accepts(attempted_len) {
return Ok(());
}
Err(RuntimeStateLimitError::new(limit, attempted_len).into())
}
pub(crate) fn ensure_return_len(
&self,
attempted_len: ReturnOutputByteCount,
) -> Result<(), RunStepError> {
let limit = self.limits.return_byte_limit();
if limit.accepts(attempted_len) {
return Ok(());
}
Err(ReturnOutputLimitError::new(limit, attempted_len).into())
}
pub(crate) fn reserve_next_step(
&mut self,
state_len: RuntimeStateByteCount,
) -> Result<StepReservation<'_>, RunStepError> {
let limit = self.limits.step_limit();
let next_step = reserve_next_step(limit, self.completed_steps, state_len)?;
Ok(StepReservation {
budget: self,
next_step,
})
}
}
impl StepReservation<'_> {
pub(crate) fn ensure_rewrite_state_len(
&self,
attempted_len: RuntimeStateByteCount,
) -> Result<(), RunStepError> {
self.budget.ensure_rewrite_state_len(attempted_len)
}
pub(crate) fn ensure_return_len(
&self,
attempted_len: ReturnOutputByteCount,
) -> Result<(), RunStepError> {
self.budget.ensure_return_len(attempted_len)
}
pub(crate) fn commit(self) -> StepCount {
self.budget.completed_steps = self.next_step;
self.next_step
}
}
impl RuleAttemptBudgetState {
pub(crate) const fn new(limit: RuleAttemptLimit) -> Self {
Self {
limit,
completed_attempts: RuleAttemptCount::ZERO,
}
}
pub(crate) const fn completed_attempts(&self) -> RuleAttemptCount {
self.completed_attempts
}
pub(crate) fn reserve_next_attempt(
&mut self,
state_len: RuntimeStateByteCount,
) -> Result<RuleAttemptReservation<'_>, RuleAttemptStepError> {
let next_attempt = reserve_next_attempt(self.limit, self.completed_attempts, state_len)?;
Ok(RuleAttemptReservation {
budget: self,
next_attempt,
})
}
}
impl RuleAttemptReservation<'_> {
pub(crate) fn commit(self) -> RuleAttemptCount {
self.budget.completed_attempts = self.next_attempt;
self.next_attempt
}
}
fn reserve_next_step(
limit: crate::limits::StepLimit,
completed_count: StepCount,
state_len: RuntimeStateByteCount,
) -> Result<StepCount, RunStepError> {
if !limit.allows_next_after(completed_count) {
return Err(StepLimitError::new(limit, completed_count, state_len).into());
}
completed_count
.checked_next()
.ok_or_else(|| StepLimitError::new(limit, completed_count, state_len).into())
}
fn reserve_next_attempt(
limit: RuleAttemptLimit,
completed_count: RuleAttemptCount,
state_len: RuntimeStateByteCount,
) -> Result<RuleAttemptCount, RuleAttemptStepError> {
if !limit.allows_next_after(completed_count) {
return Err(RuleAttemptLimitError::new(limit, completed_count, state_len).into());
}
completed_count
.checked_next()
.ok_or_else(|| RuleAttemptLimitError::new(limit, completed_count, state_len).into())
}