nefsm/
lib.rs

1//! A simple state machine library for Rust, using enums for states and events.
2//!
3//! This library provides a simple, flexible way to define state machines in Rust. The states
4//! and events are defined using Rust enums, and the state machine itself is a generic struct
5//! that can be instantiated with any specific set of enums.
6//!
7//! The core traits used in this library are `FsmEnum`, and `Stateful`.
8//!
9//! * `FsmEnum` is a trait that defines how to create a new state machine state based on a given
10//!   enum value. This is used to instantiate new state machine states when a state transition occurs.
11//!
12//! * `Stateful` is a trait that defines how a state should handle state transition events.
13//!
14//! * `StateMachine` is the main struct that represents a state machine instance. It tracks the
15//!   current state, and provides methods to initialize the state machine, process events, and get
16//!   the current state.
17//!
18//! This library is designed to be easy to use and flexible enough to handle a wide variety of
19//! state machine designs.
20//!
21//!
22
23pub mod sync {
24    use std::fmt::Debug;
25    use std::{collections::HashMap, hash::Hash};
26
27    // Define the FsmEnum trait, which is used to create new state objects
28    pub trait FsmEnum<S, CTX, E> {
29        fn create(enum_value: &S) -> Box<dyn Stateful<S, CTX, E> + Send>;
30    }
31
32    // Define the Stateful trait, which contains the event handling methods for each state
33    pub trait Stateful<S: Hash + PartialEq + Eq + Clone, CTX, E: Debug> {
34        fn on_enter(&mut self, context: &mut CTX) -> Response<S>;
35        fn on_event(&mut self, event: &E, context: &mut CTX) -> Response<S>;
36        fn on_exit(&mut self, context: &mut CTX);
37    }
38
39    // Define the EventHandler trait, which is used to handle global events
40    pub trait EventHandler<S: Hash + PartialEq + Eq + Clone, CTX, E: Debug> {
41        fn on_event(&mut self, event: &E, context: &mut CTX) -> Response<S>;
42    }
43
44    // Define the Response enum, which is used to handle state transitions
45    pub enum Response<S> {
46        Handled,
47        Transition(S),
48    }
49
50    // Define the Error enum, which is used to handle errors
51    #[derive(Debug)]
52    pub enum Error {
53        StateNotFound(String),
54        StateMachineNotInitialized,
55    }
56
57    // Define the StateMachine struct, which represents the finite state machine
58    pub struct StateMachine<S: Hash + PartialEq + Eq + Clone + FsmEnum<S, CTX, E>, CTX, E: Debug> {
59        states: HashMap<S, Box<dyn Stateful<S, CTX, E> + Send>>,
60        current_state: Option<S>,
61        context: CTX,
62        global_event_handler: Option<Box<dyn EventHandler<S, CTX, E> + Send>>,
63    }
64
65    // Implement methods for the StateMachine struct
66    impl<S: Hash + PartialEq + Eq + Clone + FsmEnum<S, CTX, E>, CTX, E: Debug> StateMachine<S, CTX, E> {
67        // Define a constructor for the StateMachine struct
68        pub fn new(context: CTX, handler: Option<Box<dyn EventHandler<S, CTX, E> + Send>>) -> Self {
69            let states = HashMap::<S, Box<dyn Stateful<S, CTX, E> + Send>>::new();
70            Self {
71                states,
72                current_state: None,
73                context,
74                global_event_handler: handler,
75            }
76        }
77
78        // Define a method to get the current state
79        pub fn get_current_state(&self) -> Option<&S> {
80            self.current_state.as_ref()
81        }
82
83        // Define a method to get a reference to the context
84        pub fn get_context(&self) -> &CTX {
85            &self.context
86        }
87
88        // Define a method to initialize the state machine with an initial state
89        // Note how the state objects are cached in a HashMap and not recreated every time we transition to this event.
90        pub fn init(&mut self, initial_state: S) -> Result<(), Error> {
91            if self.current_state.is_none() {
92                self.current_state = Some(initial_state);
93                loop {
94                    let current_state_ref = self.current_state.as_ref().unwrap();
95                    let state = if let Some(existing_state) = self.states.get_mut(current_state_ref)
96                    {
97                        existing_state
98                    } else {
99                        let new_state = S::create(current_state_ref);
100                        let current_state_clone = self.current_state.clone().unwrap();
101                        self.states.entry(current_state_clone).or_insert(new_state)
102                    };
103
104                    match state.on_enter(&mut self.context) {
105                        Response::Handled => break,
106                        Response::Transition(s) => self.current_state = Some(s),
107                    }
108                }
109            }
110            Ok(())
111        }
112
113        // Define a method to process events and transition between states
114        pub fn process_event(&mut self, event: &E) -> Result<(), Error> {
115            let c_state = match &self.current_state {
116                Some(state) => state,
117                None => return Err(Error::StateMachineNotInitialized),
118            };
119
120            if let Some(global_handler) = &mut self.global_event_handler {
121                match global_handler.on_event(event, &mut self.context) {
122                    Response::Handled => {}
123                    Response::Transition(new_state) => {
124                        if new_state != *c_state {
125                            return self.transition_to(new_state);
126                        }
127                    }
128                }
129            }
130
131            let current_state_ref = self.current_state.as_ref().unwrap();
132            let state = if let Some(existing_state) = self.states.get_mut(current_state_ref) {
133                existing_state
134            } else {
135                let new_state = S::create(current_state_ref);
136                let current_state_clone = self.current_state.clone().unwrap();
137                self.states.entry(current_state_clone).or_insert(new_state)
138            };
139            match state.on_event(event, &mut self.context) {
140                Response::Handled => {}
141                Response::Transition(new_state) => {
142                    if new_state != *c_state {
143                        self.transition_to(new_state)?;
144                    }
145                }
146            }
147
148            Ok(())
149        }
150
151        // Define a method to handle state transitions
152        fn transition_to(&mut self, new_state: S) -> Result<(), Error> {
153            let c_state = self.current_state.as_ref().unwrap();
154            let state = self.states.get_mut(&c_state).unwrap();
155            state.on_exit(&mut self.context);
156
157            self.current_state = Some(new_state.clone());
158            loop {
159                let current_state_ref = self.current_state.as_ref().unwrap();
160                let s = if let Some(existing_state) = self.states.get_mut(current_state_ref) {
161                    existing_state
162                } else {
163                    let new_state = S::create(current_state_ref);
164                    let current_state_clone = self.current_state.clone().unwrap();
165                    self.states.entry(current_state_clone).or_insert(new_state)
166                };
167                match s.on_enter(&mut self.context) {
168                    Response::Handled => {
169                        break;
170                    }
171                    Response::Transition(s) => {
172                        if s == *self.current_state.as_ref().unwrap() {
173                            break;
174                        } else {
175                            self.current_state = Some(s);
176                        }
177                    }
178                }
179            }
180
181            Ok(())
182        }
183    }
184}
185pub mod Async {
186    use std::fmt::Debug;
187    use std::{collections::HashMap, hash::Hash};
188
189    use async_trait::async_trait;
190
191    // Define the FsmEnum trait, which is used to create new state objects
192    pub trait FsmEnum<S, CTX, E> {
193        fn create(enum_value: &S) -> Box<dyn Stateful<S, CTX, E> + Send>;
194    }
195
196    // Define the EventHandler trait for handling global events
197    #[async_trait]
198    pub trait EventHandler<S: Hash + PartialEq + Eq + Clone, CTX, E: Debug> {
199        async fn on_event(&mut self, event: &E, context: &mut CTX) -> Response<S>;
200    }
201
202    // Define the Stateful trait, which contains the event handling methods for each state
203    #[async_trait]
204    pub trait Stateful<S: Hash + PartialEq + Eq + Clone, CTX, E: Debug> {
205        async fn on_enter(&mut self, context: &mut CTX) -> Response<S>;
206        async fn on_event(&mut self, event: &E, context: &mut CTX) -> Response<S>;
207        async fn on_exit(&mut self, context: &mut CTX);
208    }
209
210    // Define the Response enum, which is used to handle state transitions
211    pub enum Response<S> {
212        Handled,
213        Transition(S),
214    }
215
216    // Define the Error enum, which is used to handle errors
217    #[derive(Debug)]
218    pub enum Error {
219        StateNotFound(String),
220        StateMachineNotInitialized,
221    }
222
223    // Define the StateMachine struct, which represents the finite state machine
224    pub struct StateMachine<S: Hash + PartialEq + Eq + Clone + FsmEnum<S, CTX, E>, CTX, E: Debug> {
225        states: HashMap<S, Box<dyn Stateful<S, CTX, E> + Send>>,
226        current_state: Option<S>,
227        context: CTX,
228        global_event_handler: Option<Box<dyn EventHandler<S, CTX, E> + Send>>,
229    }
230
231    // Implement methods for the StateMachine struct
232    impl<S: Hash + PartialEq + Eq + Clone + FsmEnum<S, CTX, E>, CTX, E: Debug> StateMachine<S, CTX, E> {
233        // Define a constructor for the StateMachine struct
234        pub fn new(
235            context: CTX,
236            global_handler: Option<Box<dyn EventHandler<S, CTX, E> + Send>>,
237        ) -> Self {
238            Self {
239                states: HashMap::new(),
240                current_state: None,
241                context,
242                global_event_handler: global_handler,
243            }
244        }
245
246        // Define a method to get the current state
247        pub fn get_current_state(&self) -> Option<&S> {
248            self.current_state.as_ref()
249        }
250
251        // Define a method to get a reference to the context
252        pub fn get_context(&self) -> &CTX {
253            &self.context
254        }
255
256        // Define a method to initialize the state machine with an initial state
257        pub async fn init(&mut self, initial_state: S) -> Result<(), Error> {
258            if self.current_state.is_none() {
259                self.current_state = Some(initial_state);
260                loop {
261                    let current_state_ref = self.current_state.as_ref().unwrap();
262                    let state = if let Some(existing_state) = self.states.get_mut(current_state_ref)
263                    {
264                        existing_state
265                    } else {
266                        let new_state = S::create(current_state_ref);
267                        let current_state_clone = self.current_state.clone().unwrap();
268                        self.states.entry(current_state_clone).or_insert(new_state)
269                    };
270
271                    match state.on_enter(&mut self.context).await {
272                        Response::Handled => break,
273                        Response::Transition(s) => self.current_state = Some(s),
274                    }
275                }
276            }
277            Ok(())
278        }
279        // Define a method to process events and transition between states
280        pub async fn process_event(&mut self, event: &E) -> Result<(), Error> {
281            let c_state = match &self.current_state {
282                Some(state) => state,
283                None => return Err(Error::StateMachineNotInitialized),
284            };
285
286            if let Some(ref mut handler) = self.global_event_handler {
287                match handler.on_event(event, &mut self.context).await {
288                    Response::Handled => {}
289                    Response::Transition(new_state) => {
290                        if new_state != *c_state {
291                            self.transition_to(new_state).await;
292                            return Ok(());
293                        }
294                    }
295                }
296            }
297
298            let current_state_ref = self.current_state.as_ref().unwrap();
299            let state = if let Some(existing_state) = self.states.get_mut(current_state_ref) {
300                existing_state
301            } else {
302                let new_state = S::create(current_state_ref);
303                let current_state_clone = self.current_state.clone().unwrap();
304                self.states.entry(current_state_clone).or_insert(new_state)
305            };
306
307            match state.on_event(event, &mut self.context).await {
308                Response::Handled => {}
309                Response::Transition(new_state) => {
310                    if new_state != *c_state {
311                        self.transition_to(new_state).await;
312                    }
313                }
314            }
315
316            Ok(())
317        }
318
319        async fn transition_to(&mut self, new_state: S) {
320            let c_state = self.current_state.as_ref().unwrap();
321            let state = self.states.get_mut(&c_state).unwrap();
322            state.on_exit(&mut self.context).await;
323
324            self.current_state = Some(new_state);
325
326            loop {
327                let current_state_ref = self.current_state.as_ref().unwrap();
328                let s = if let Some(existing_state) = self.states.get_mut(current_state_ref) {
329                    existing_state
330                } else {
331                    let new_state = S::create(current_state_ref);
332                    let current_state_clone = self.current_state.clone().unwrap();
333                    self.states.entry(current_state_clone).or_insert(new_state)
334                };
335
336                match s.on_enter(&mut self.context).await {
337                    Response::Handled => {
338                        break;
339                    }
340                    Response::Transition(s) => {
341                        if s == *current_state_ref {
342                            break;
343                        } else {
344                            self.current_state = Some(s);
345                        }
346                    }
347                }
348            }
349        }
350    }
351}