1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

//! A simple state machine library for Rust, using enums for states and events.
//!
//! This library provides a simple, flexible way to define state machines in Rust. The states
//! and events are defined using Rust enums, and the state machine itself is a generic struct
//! that can be instantiated with any specific set of enums.
//!
//! The core traits used in this library are `FsmEnum`, and `Stateful`.
//!
//! * `FsmEnum` is a trait that defines how to create a new state machine state based on a given
//!   enum value. This is used to instantiate new state machine states when a state transition occurs.
//!
//! * `Stateful` is a trait that defines how a state should handle state transition events.
//!
//! * `StateMachine` is the main struct that represents a state machine instance. It tracks the
//!   current state, and provides methods to initialize the state machine, process events, and get
//!   the current state.
//!
//! This library is designed to be easy to use and flexible enough to handle a wide variety of
//! state machine designs.
//!
//! 

use std::fmt::Debug;
use std::{collections::HashMap, hash::Hash};


// Define the FsmEnum trait, which is used to create new state objects
pub trait FsmEnum<S, CTX, E> {
    fn create(enum_value: &S) -> Box<dyn Stateful<S, CTX, E> + Send>;
}

// Define the Stateful trait, which contains the event handling methods for each state
pub trait Stateful<S: Hash + PartialEq + Eq + Clone, CTX, E: Debug> {
    fn on_enter(&mut self, context: &mut CTX) -> Response<S>;
    fn on_event(&mut self, event: &E, context: &mut CTX) -> Response<S>;
    fn on_exit(&mut self, context: &mut CTX);
}

// Define the Response enum, which is used to handle state transitions
pub enum Response<S> {
    Handled,
    Transition(S),
}
// Define the Error enum, which is used to handle errors
#[derive(Debug)]
pub enum Error {
    StateNotFound(String),
    StateMachineNotInitialized,
}

// Define the StateMachine struct, which represents the finite state machine
pub struct StateMachine<
    S: Hash + PartialEq + Eq + Clone + FsmEnum<S, CTX, E>,
    CTX,
    E: Debug,
> {
    states: HashMap<S, Box<dyn Stateful<S, CTX, E> + Send>>,
    current_state: Option<S>,
    context: CTX,
}

// Implement methods for the StateMachine struct
impl<S: Hash + PartialEq + Eq + Clone + FsmEnum<S, CTX, E>, CTX, E: Debug>
    StateMachine<S, CTX, E>
where
    S: Debug + Send,
    CTX: Sized,
    E: Sized,
{
    // Define a constructor for the StateMachine struct
    pub fn new(context: CTX) -> Self {
        let states = HashMap::<S, Box<dyn Stateful<S, CTX, E> + Send>>::new();
        Self {
            states: states,
            current_state: None,
            context: context,
        }
    }

    // Define a method to get the current state
    pub fn get_current_state(&self) -> Option<&S> {
        self.current_state.as_ref()
    }
    
    // Define a method to get a reference to the context
    pub fn get_context(&self) -> &CTX {
        &self.context
    }


    // Define a method to initialize the state machine with an initial state
    // Note how the state objects are cached in a HashMap and not recreated every time we transition to this event.
    pub fn init(&mut self, initial_state: S) -> Result<(), Error> {
        if self.current_state.is_none() {
            self.current_state = Some(initial_state.clone());
            loop {                
                let state = self
                .states
                .entry(self.current_state.clone().unwrap())
                .or_insert_with(|| S::create(&self.current_state.clone().unwrap()));

                match state.on_enter(&mut self.context) {
                    Response::Handled => break,
                    Response::Transition(s) => self.current_state = Some(s),
                }
            }
        }
        Ok(())
    }

    // Define a method to process events and transition between states
    pub fn process_event(&mut self, event: &E) -> Result<(), Error> {
        let c_state = match self.current_state.clone() {
            Some(state) => state,
            None => return Err(Error::StateMachineNotInitialized),
        };

        let state = self
            .states
            .entry(c_state.clone())
            .or_insert_with(|| S::create(&c_state));
        match state.on_event(event, &mut self.context) {
            Response::Handled => {}
            Response::Transition(new_state) => {
                if new_state != c_state {
                    state.on_exit(&mut &mut self.context);
                    self.current_state = Some(new_state.clone());
                    loop {
                        let s = self
                            .states
                            .entry(self.current_state.clone().unwrap())
                            .or_insert_with(|| S::create(&self.current_state.clone().unwrap()));
                        match s.on_enter(&mut self.context) {
                            Response::Handled => {
                                break;
                            }
                            Response::Transition(s) => {
                                if s == self.current_state.clone().unwrap() {
                                    break;
                                } else {                                
                                    self.current_state = Some(s);
                                }
                            }
                        }
                    }
                }
            }
        }

        Ok(())
    }
}