rsaeb 0.5.0

A no_std + alloc interpreter for A=B ordered rewrite programs.
Documentation
use alloc::vec::Vec;

use crate::allocation::{AllocationContext, AllocationError, try_push, try_reserve_total_exact};
use crate::error::RuntimeInvariantError;
use crate::rule::{OnceRuleSlot, OnceRuleSlotCount, RuleSchedule};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum RuleEligibility {
    Eligible(MatchedRuleSchedule),
    ConsumedOnce,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum MatchedRuleSchedule {
    Always,
    Once(OnceRuleSlot),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum OnceRuleState {
    Fresh,
    Consumed,
}

#[derive(Debug, PartialEq, Eq)]
pub(super) struct OnceRunStates {
    states: Vec<OnceRuleState>,
}

impl OnceRunStates {
    pub(super) fn new(once_slot_count: OnceRuleSlotCount) -> Result<Self, AllocationError> {
        let mut states = Vec::new();
        let state_count = once_slot_count.get();
        try_reserve_total_exact(
            &mut states,
            state_count,
            AllocationContext::RuntimeOnceRuleState,
        )?;

        for _ in 0..state_count {
            try_push(
                &mut states,
                OnceRuleState::Fresh,
                AllocationContext::RuntimeOnceRuleState,
            )?;
        }

        Ok(Self { states })
    }

    pub(super) fn eligibility(
        &self,
        schedule: RuleSchedule,
    ) -> Result<RuleEligibility, RuntimeInvariantError> {
        match schedule {
            RuleSchedule::Always => Ok(RuleEligibility::Eligible(MatchedRuleSchedule::Always)),
            RuleSchedule::Once(slot) => {
                let once_state_count = self.states.len();
                let state = self.states.get(slot.get()).copied().ok_or_else(|| {
                    RuntimeInvariantError::missing_once_rule_state(slot.get(), once_state_count)
                })?;

                match state {
                    OnceRuleState::Fresh => {
                        Ok(RuleEligibility::Eligible(MatchedRuleSchedule::Once(slot)))
                    }
                    OnceRuleState::Consumed => Ok(RuleEligibility::ConsumedOnce),
                }
            }
        }
    }

    pub(super) fn consume(
        &mut self,
        schedule: MatchedRuleSchedule,
    ) -> Result<(), RuntimeInvariantError> {
        match schedule {
            MatchedRuleSchedule::Always => Ok(()),
            MatchedRuleSchedule::Once(slot) => {
                let once_state_count = self.states.len();
                let state = self.states.get_mut(slot.get()).ok_or_else(|| {
                    RuntimeInvariantError::missing_once_rule_state(slot.get(), once_state_count)
                })?;

                match state {
                    OnceRuleState::Fresh => {
                        *state = OnceRuleState::Consumed;
                        Ok(())
                    }
                    OnceRuleState::Consumed => {
                        Err(RuntimeInvariantError::consumed_once_rule_slot(slot.get()))
                    }
                }
            }
        }
    }
}