bombay-behavior 0.11.0

Composable, statically typed actor behavior algebra
Documentation
//! A finite-state behavior derived solely from receive and become.

use std::collections::VecDeque;

use crate::Exit;
use crate::behavior::{Actions, Address, Behavior, NoBirths, User};
use crate::next::{Never, Step};

pub enum Move<P> {
    Stay,
    Goto(P),
    Defer,
    Stop,
}

enum Advance<A: Address> {
    Continue,
    PhaseChanged,
    Stop(Exit<A>),
}

pub struct Machine<A: Address, S, M, P, E> {
    state: S,
    phase: P,
    on: fn(P, &mut S, &M) -> Result<Move<P>, E>,
    held: VecDeque<M>,
    address: core::marker::PhantomData<A>,
}

impl<A, S, M, P, E> crate::BehaviorBase for Machine<A, S, M, P, E>
where
    A: Address,
    P: Copy + PartialEq,
{
    type Base = Self;

    fn base(&self) -> &Self {
        self
    }
}

impl<A: Address, S, M, P: Copy + PartialEq, E> Machine<A, S, M, P, E> {
    #[must_use]
    pub fn new(state: S, phase: P, on: fn(P, &mut S, &M) -> Result<Move<P>, E>) -> Self {
        Self {
            state,
            phase,
            on,
            held: VecDeque::new(),
            address: core::marker::PhantomData,
        }
    }

    #[must_use]
    pub fn state(&self) -> &S {
        &self.state
    }

    #[must_use]
    pub fn phase(&self) -> P {
        self.phase
    }

    #[must_use]
    pub fn held(&self) -> usize {
        self.held.len()
    }

    fn advance(&mut self, message: M) -> Result<Advance<A>, E> {
        Ok(match (self.on)(self.phase, &mut self.state, &message)? {
            Move::Stay => Advance::Continue,
            Move::Defer => {
                self.held.push_back(message);
                Advance::Continue
            }
            Move::Stop => Advance::Stop(Exit::Normal),
            Move::Goto(next) => {
                let changed = next != self.phase;
                self.phase = next;
                if changed {
                    Advance::PhaseChanged
                } else {
                    Advance::Continue
                }
            }
        })
    }

    fn drain(&mut self) -> Result<Step<Never, Exit<A>>, E> {
        let mut batch: VecDeque<M> = self.held.drain(..).collect();
        while let Some(message) = batch.pop_front() {
            let outcome = match self.advance(message) {
                Ok(transition) => transition,
                Err(error) => {
                    self.held.extend(batch);
                    return Err(error);
                }
            };
            match outcome {
                Advance::Continue => {}
                Advance::PhaseChanged => batch.extend(self.held.drain(..)),
                Advance::Stop(exit) => {
                    self.held.extend(batch);
                    return Ok(Step::Stop(exit));
                }
            }
        }
        Ok(Step::Continue)
    }
}

impl<A, S, M, P, E> Behavior for Machine<A, S, M, P, E>
where
    A: Address,
    P: Copy + PartialEq,
{
    type Addr = A;
    type Msg = M;
    type Event = User<A, M>;
    type Sends = Vec<Never>;
    type Ph = Never;
    type Error = E;
    type Birth = NoBirths;

    fn init(
        &mut self,
        _: crate::InitializationTurn,
    ) -> Result<Actions<A, Never, Self::Sends, NoBirths>, E> {
        Ok(Actions::cont())
    }

    fn transition(
        &mut self,
        _: crate::ActiveTurn,
        event: Self::Event,
    ) -> Result<Actions<A, Never, Self::Sends, NoBirths>, E> {
        match self.advance(event.message)? {
            Advance::Stop(exit) => Ok(Actions::stop(exit)),
            Advance::PhaseChanged => Ok(Actions::just(self.drain()?)),
            Advance::Continue => Ok(Actions::cont()),
        }
    }
}