1use 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
15pub 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}