automata_like_programming/
automaton.rs

1use std::{marker::PhantomData, rc::Rc};
2
3use crate::automaton_state::SharedAutomatonState;
4
5/// Result of an attempt of determining next target state.
6pub enum NextState<'a, Id, D, E> {
7    /// Automaton should take provided state for the next iteration.
8    Continue(SharedAutomatonState<'a, Id, D, E>),
9    /// The input data has ended so there is no way for matching next state.
10    ProcessEnded,
11    /// There are no possible target states for received input data.
12    NotFound,
13}
14
15/// Iterator for providing next key.
16pub trait KeyIter<K> {
17    fn next(&mut self) -> Option<K>;
18}
19
20/// Finite-state automaton that crawls around a specified graph until no more state changes can be done.
21pub struct Automaton<'a, Id, D, E> {
22    start_state: SharedAutomatonState<'a, Id, D, E>,
23    _data_phantom: PhantomData<D>,
24    _error_phantom: PhantomData<E>,
25}
26
27/// Provides information on why automaton has stopped executing.
28pub enum AutomatonResult<Id, E> {
29    // Ok, // Not needed - should end because no more keys, no state could be found or state forces the end of process (no default ending).
30    /// Automaton execution ended because no more keys could be extracted. Contains identifier of current state in automaton execution - no more
31    /// keys could be extracted after reaching this state.
32    EmptyIter(
33        Id
34    ),
35    /// No connection could be matched for a key. Contains identifier of current state in automaton execution - no connections could be found on this state for given key.
36    CouldNotFindNextState(
37        Id
38    ),
39    /// An error occured while executing function assigned to connection. Contains error generated while changing state.
40    Error(
41        E
42    )
43}
44
45impl <Id, E> AutomatonResult<Id, E> {
46    pub fn is_empty_iter(&self) -> bool {
47        return matches!(self, AutomatonResult::EmptyIter(_))
48    }
49
50    pub fn is_could_not_find_next_state(&self) -> bool {
51        return matches!(self, AutomatonResult::CouldNotFindNextState(_))
52    }
53
54    pub fn is_error(&self) -> bool {
55        return matches!(self, AutomatonResult::Error(_))
56    }
57}
58
59impl <'a, Id, D, E> Automaton<'a, Id, D, E> {
60    /// Creates new automaton with graph initiated by specified function.
61    pub fn new<FInit: Fn() -> SharedAutomatonState<'a, Id, D, E>>(f_state_graph_init: FInit) -> Self {
62        Self {start_state: f_state_graph_init(), _data_phantom: PhantomData{}, _error_phantom: PhantomData{}}
63    }
64
65    /// Starts automaton with given data.
66    pub fn run(&mut self, data: &mut D) -> AutomatonResult<Id, E> {
67        let mut current_state = Rc::clone(&self.start_state);
68        loop {
69            let connection_execute_result = current_state.borrow().execute_next_connection(data);
70            match connection_execute_result {
71                Err(err) => {
72                    return AutomatonResult::Error(err);
73                },
74                Ok(next_state_result) => {
75                    match next_state_result {
76                        NextState::Continue(next_state) => current_state = next_state,
77                        NextState::NotFound => return AutomatonResult::CouldNotFindNextState(current_state.borrow().get_id_owned()),
78                        NextState::ProcessEnded => return AutomatonResult::EmptyIter(current_state.borrow().get_id_owned()),
79                    };
80                },
81            };
82        };
83    }
84}
85
86#[cfg(test)]
87pub mod test {
88    use std::rc::Rc;
89
90    use crate::{automaton::AutomatonResult, automaton_state::{new_shared_automaton_state, AutomatonState, SharedAutomatonState}};
91
92    use super::{Automaton, NextState};
93
94    pub struct TestNodeHello<'a> {
95        next_state: Option<SharedAutomatonState<'a, u8, String, String>>
96    }
97
98    impl<'a> TestNodeHello <'a> {
99        pub fn new(next_state: Option<SharedAutomatonState<'a, u8, String, String>>) -> Self {
100            Self { next_state }
101        }
102    }
103
104    impl <'a> AutomatonState<'a, u8, String, String> for TestNodeHello<'a> {
105        fn get_id_owned(&self) -> u8 {
106            1
107        }
108        
109        fn get_id(&self) -> &u8 {
110            &1
111        }
112        
113        fn execute_next_connection(&self, data: &mut String) -> Result<NextState<'a, u8, String, String>, String> {
114            data.push_str("Hello");
115            if let Option::Some(nxt_state) = &self.next_state {
116                Result::Ok(NextState::Continue(Rc::clone(nxt_state)))
117            } else {
118                Result::Ok(NextState::NotFound)
119            }
120        }
121    }
122
123    pub struct TestNodeWorld {
124    }
125
126    impl TestNodeWorld {
127        pub fn new() -> Self {
128            Self {  }
129        }
130    }
131
132    impl <'a> AutomatonState<'a, u8, String, String> for TestNodeWorld {
133        fn get_id_owned(&self) -> u8 {
134            2
135        }
136        
137        fn get_id(&self) -> &u8 {
138            &2
139        }
140        
141        fn execute_next_connection(&self, data: &mut String) -> Result<NextState<'a, u8, String, String>, String> {
142            data.push_str(" world");
143            Result::Ok(NextState::ProcessEnded)
144        }
145    }
146
147    #[test]
148    fn automaton_2_nodes_works() -> () {
149        let mut data = String::with_capacity(11);
150        let mut automaton = Automaton::new(|| {
151            let world_state: SharedAutomatonState<u8, String, _> = new_shared_automaton_state(TestNodeWorld::new());
152            let hello_state: SharedAutomatonState<u8, String, _> = new_shared_automaton_state(TestNodeHello::new(Option::Some(Rc::clone(&world_state))));
153            hello_state
154        });
155        let run_res = automaton.run(&mut data);
156        assert!(matches!(run_res, AutomatonResult::EmptyIter(2)));
157        assert_eq!(data, "Hello world");
158    }
159
160    #[test]
161    fn automaton_result_is_empty_iter() -> () {
162        assert!(AutomatonResult::<u8, String>::EmptyIter(1).is_empty_iter());
163        assert!(!AutomatonResult::<u8, String>::CouldNotFindNextState(1).is_empty_iter());
164        assert!(!AutomatonResult::<u8, String>::Error(String::from("Test error")).is_empty_iter());
165    }
166
167    #[test]
168    fn automaton_result_is_could_not_find_next_state() -> () {
169        assert!(!AutomatonResult::<u8, String>::EmptyIter(1).is_could_not_find_next_state());
170        assert!(AutomatonResult::<u8, String>::CouldNotFindNextState(1).is_could_not_find_next_state());
171        assert!(!AutomatonResult::<u8, String>::Error(String::from("Test error")).is_could_not_find_next_state());
172    }
173
174    #[test]
175    fn automaton_result_is_error() -> () {
176        assert!(!AutomatonResult::<u8, String>::EmptyIter(1).is_error());
177        assert!(!AutomatonResult::<u8, String>::CouldNotFindNextState(1).is_error());
178        assert!(AutomatonResult::<u8, String>::Error(String::from("Test error")).is_error());
179    }
180}