Skip to main content

atomr_core/actor/
fsm.rs

1//! Finite state machine DSL. akka.net: `Actor/FSM.cs`.
2//!
3//! See also the [`fsm!`](crate::fsm) macro for a terse table-style
4//! `FiniteStateMachine` impl.
5
6use std::time::Duration;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct FsmTransition<S, D> {
10    pub next: S,
11    pub data: D,
12    pub timeout: Option<Duration>,
13}
14
15/// Simple trait-based FSM. Actors implementing this trait are driven by
16/// `ctx.become(...)` inside their cell.
17pub trait FiniteStateMachine {
18    type State: Clone + Eq + 'static;
19    type Data: Clone + 'static;
20    type Msg: Send + 'static;
21
22    fn initial_state(&self) -> Self::State;
23    fn initial_data(&self) -> Self::Data;
24
25    fn transition(
26        &mut self,
27        current: &Self::State,
28        data: &Self::Data,
29        msg: Self::Msg,
30    ) -> Option<FsmTransition<Self::State, Self::Data>>;
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36
37    #[derive(Clone, Eq, PartialEq, Debug)]
38    enum S {
39        Idle,
40        Running,
41    }
42
43    struct TrafficLight;
44    enum M {
45        Go,
46        Stop,
47    }
48
49    impl FiniteStateMachine for TrafficLight {
50        type State = S;
51        type Data = u32;
52        type Msg = M;
53
54        fn initial_state(&self) -> S {
55            S::Idle
56        }
57        fn initial_data(&self) -> u32 {
58            0
59        }
60
61        fn transition(&mut self, s: &S, d: &u32, m: M) -> Option<FsmTransition<S, u32>> {
62            match (s, m) {
63                (S::Idle, M::Go) => Some(FsmTransition { next: S::Running, data: d + 1, timeout: None }),
64                (S::Running, M::Stop) => Some(FsmTransition { next: S::Idle, data: *d, timeout: None }),
65                _ => None,
66            }
67        }
68    }
69
70    #[test]
71    fn transitions_idle_to_running() {
72        let mut fsm = TrafficLight;
73        let t = fsm.transition(&S::Idle, &0, M::Go).unwrap();
74        assert_eq!(t.next, S::Running);
75        assert_eq!(t.data, 1);
76    }
77
78    #[test]
79    fn transitions_running_to_idle_on_stop() {
80        let mut fsm = TrafficLight;
81        let t = fsm.transition(&S::Running, &5, M::Stop).unwrap();
82        assert_eq!(t.next, S::Idle);
83        assert_eq!(t.data, 5);
84    }
85}