use crate::bytes::{ReturnOutputByteCount, RuntimeStateByteCount};
use crate::error::{
ReturnOutputLimitError, RuleAttemptLimitError, RuleAttemptStepError, RunStepError,
RuntimeStateLimitError, StepLimitError,
};
use crate::limits::{
ExecutionLimits, ReturnByteLimit, RuleAttemptCount, RuleAttemptLimit, RuntimeStateByteLimit,
StepCount, StepLimit,
};
trait RuntimeByteLimit<Count>: Copy {
fn accepts_count(self, attempted_len: Count) -> bool;
fn limit_error(self, attempted_len: Count) -> RunStepError;
}
trait ReservationLimit<Count>: Copy {
type Error;
fn allows_next_after_count(self, completed_count: Count) -> bool;
fn limit_error(self, completed_count: Count, state_len: RuntimeStateByteCount) -> Self::Error;
}
trait ReservableCount: Copy {
fn checked_next_count(self) -> Option<Self>;
}
#[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> {
ensure_runtime_byte_limit(self.limits.state_byte_limit(), attempted_len)
}
pub(crate) fn ensure_return_len(
&self,
attempted_len: ReturnOutputByteCount,
) -> Result<(), RunStepError> {
ensure_runtime_byte_limit(self.limits.return_byte_limit(), attempted_len)
}
pub(crate) fn reserve_next_step(
&mut self,
state_len: RuntimeStateByteCount,
) -> Result<StepReservation<'_>, RunStepError> {
let next_step =
reserve_next_count(self.limits.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_count(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
}
}
impl RuntimeByteLimit<RuntimeStateByteCount> for RuntimeStateByteLimit {
fn accepts_count(self, attempted_len: RuntimeStateByteCount) -> bool {
self.accepts(attempted_len)
}
fn limit_error(self, attempted_len: RuntimeStateByteCount) -> RunStepError {
RuntimeStateLimitError::new(self, attempted_len).into()
}
}
impl RuntimeByteLimit<ReturnOutputByteCount> for ReturnByteLimit {
fn accepts_count(self, attempted_len: ReturnOutputByteCount) -> bool {
self.accepts(attempted_len)
}
fn limit_error(self, attempted_len: ReturnOutputByteCount) -> RunStepError {
ReturnOutputLimitError::new(self, attempted_len).into()
}
}
impl ReservationLimit<StepCount> for StepLimit {
type Error = RunStepError;
fn allows_next_after_count(self, completed_count: StepCount) -> bool {
self.allows_next_after(completed_count)
}
fn limit_error(
self,
completed_count: StepCount,
state_len: RuntimeStateByteCount,
) -> Self::Error {
StepLimitError::new(self, completed_count, state_len).into()
}
}
impl ReservationLimit<RuleAttemptCount> for RuleAttemptLimit {
type Error = RuleAttemptStepError;
fn allows_next_after_count(self, completed_count: RuleAttemptCount) -> bool {
self.allows_next_after(completed_count)
}
fn limit_error(
self,
completed_count: RuleAttemptCount,
state_len: RuntimeStateByteCount,
) -> Self::Error {
RuleAttemptLimitError::new(self, completed_count, state_len).into()
}
}
impl ReservableCount for StepCount {
fn checked_next_count(self) -> Option<Self> {
self.checked_next()
}
}
impl ReservableCount for RuleAttemptCount {
fn checked_next_count(self) -> Option<Self> {
self.checked_next()
}
}
fn ensure_runtime_byte_limit<Limit, Count>(
limit: Limit,
attempted_len: Count,
) -> Result<(), RunStepError>
where
Limit: RuntimeByteLimit<Count>,
Count: Copy,
{
if limit.accepts_count(attempted_len) {
return Ok(());
}
Err(limit.limit_error(attempted_len))
}
fn reserve_next_count<Limit, Count>(
limit: Limit,
completed_count: Count,
state_len: RuntimeStateByteCount,
) -> Result<Count, <Limit as ReservationLimit<Count>>::Error>
where
Limit: ReservationLimit<Count>,
Count: ReservableCount,
{
if !limit.allows_next_after_count(completed_count) {
return Err(limit.limit_error(completed_count, state_len));
}
completed_count
.checked_next_count()
.ok_or_else(|| limit.limit_error(completed_count, state_len))
}