Skip to main content

behavior_actors/
machine.rs

1//! A finite-state behavior derived solely from receive and become.
2
3use std::collections::VecDeque;
4
5use behavior::{Actions, Address, Behavior, NoBirths, User};
6use behavior::{Never, Step, Stopped};
7
8pub enum Move<P> {
9    Stay,
10    Goto(P),
11    Defer,
12    Stop,
13}
14
15/// Controlled machine rejection with the complete unaccepted mailbox input.
16#[derive(Clone, PartialEq, Eq, thiserror::Error)]
17#[error("machine rejected its mailbox input: {cause}")]
18pub struct MachineError<A, M, E> {
19    /// Exact user event whose transition or induced drain was rejected.
20    pub event: User<A, M>,
21    /// Domain error returned by the phase function.
22    #[source]
23    pub cause: E,
24}
25
26impl<A, M, E: core::fmt::Debug> core::fmt::Debug for MachineError<A, M, E> {
27    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
28        formatter
29            .debug_struct("MachineError")
30            .field("event", &"<retained>")
31            .field("cause", &self.cause)
32            .finish()
33    }
34}
35
36enum Advance {
37    Continue,
38    PhaseChanged,
39    Stop,
40}
41
42pub struct Machine<A: Address, S, M, P, E> {
43    state: S,
44    phase: P,
45    on: fn(P, &mut S, &M) -> Result<Move<P>, E>,
46    held: VecDeque<M>,
47    address: core::marker::PhantomData<A>,
48}
49
50impl<A, S, M, P, E> Clone for Machine<A, S, M, P, E>
51where
52    A: Address,
53    S: Clone,
54    M: Clone,
55    P: Clone,
56{
57    fn clone(&self) -> Self {
58        Self {
59            state: self.state.clone(),
60            phase: self.phase.clone(),
61            on: self.on,
62            held: self.held.clone(),
63            address: core::marker::PhantomData,
64        }
65    }
66}
67
68impl<A, S, M, P, E> behavior::BehaviorBase for Machine<A, S, M, P, E>
69where
70    A: Address,
71{
72    type Base = Self;
73
74    fn base(&self) -> &Self {
75        self
76    }
77}
78
79impl<A: Address, S, M, P: Copy + PartialEq, E> Machine<A, S, M, P, E> {
80    #[must_use]
81    pub fn new(state: S, phase: P, on: fn(P, &mut S, &M) -> Result<Move<P>, E>) -> Self {
82        Self {
83            state,
84            phase,
85            on,
86            held: VecDeque::new(),
87            address: core::marker::PhantomData,
88        }
89    }
90
91    #[must_use]
92    pub fn state(&self) -> &S {
93        &self.state
94    }
95
96    #[must_use]
97    pub fn phase(&self) -> P {
98        self.phase
99    }
100
101    #[must_use]
102    pub fn held(&self) -> usize {
103        self.held.len()
104    }
105
106    fn advance(&mut self, message: M) -> Result<Advance, E> {
107        Ok(match (self.on)(self.phase, &mut self.state, &message)? {
108            Move::Stay => Advance::Continue,
109            Move::Defer => {
110                self.held.push_back(message);
111                Advance::Continue
112            }
113            Move::Stop => Advance::Stop,
114            Move::Goto(next) => {
115                let changed = next != self.phase;
116                self.phase = next;
117                if changed {
118                    Advance::PhaseChanged
119                } else {
120                    Advance::Continue
121                }
122            }
123        })
124    }
125
126    fn drain(&mut self) -> Result<Step<Never, Stopped>, E> {
127        let mut batch = core::mem::take(&mut self.held);
128        while let Some(message) = batch.pop_front() {
129            let outcome = match self.advance(message) {
130                Ok(transition) => transition,
131                Err(error) => {
132                    self.held.extend(batch);
133                    return Err(error);
134                }
135            };
136            match outcome {
137                Advance::Continue => {}
138                Advance::PhaseChanged => batch.extend(self.held.drain(..)),
139                Advance::Stop => {
140                    self.held.extend(batch);
141                    return Ok(Step::Stop(Stopped));
142                }
143            }
144        }
145        Ok(Step::Continue)
146    }
147}
148
149impl<A: Address, S, M, P, E> behavior::Protocol for Machine<A, S, M, P, E> {
150    type Addr = A;
151    type Msg = M;
152}
153
154impl<A, S, M, P, E> Behavior for Machine<A, S, M, P, E>
155where
156    A: Address,
157    S: Clone,
158    M: Clone,
159    P: Copy + PartialEq,
160{
161    type Protocol = Self;
162    type Event = User<A, M>;
163    type Sends = Vec<Never>;
164    type Ph = Never;
165    type Error = MachineError<A, M, E>;
166    type Birth = NoBirths;
167
168    fn init(
169        &mut self,
170        _: behavior::InitializationTurn,
171    ) -> Result<Actions<A, Never, Self::Sends, NoBirths>, Self::Error> {
172        Ok(Actions::cont())
173    }
174
175    fn transition(
176        &mut self,
177        _: behavior::ActiveTurn,
178        event: Self::Event,
179    ) -> Result<Actions<A, Never, Self::Sends, NoBirths>, Self::Error> {
180        let mut staged = self.clone();
181        let actions = match staged.advance(event.message.clone()) {
182            Err(cause) => return Err(MachineError { event, cause }),
183            Ok(Advance::Stop) => Actions::stop(),
184            Ok(Advance::PhaseChanged) => match staged.drain() {
185                Ok(step) => Actions::just(step),
186                Err(cause) => return Err(MachineError { event, cause }),
187            },
188            Ok(Advance::Continue) => Actions::cont(),
189        };
190        *self = staged;
191        Ok(actions)
192    }
193}