automata_like_programming/
automaton.rs1use std::{marker::PhantomData, rc::Rc};
2
3use crate::automaton_state::SharedAutomatonState;
4
5pub enum NextState<'a, Id, D, E> {
7 Continue(SharedAutomatonState<'a, Id, D, E>),
9 ProcessEnded,
11 NotFound,
13}
14
15pub struct Automaton<'a, Id, D, E> {
17 start_state: SharedAutomatonState<'a, Id, D, E>,
18 _data_phantom: PhantomData<D>,
19 _error_phantom: PhantomData<E>,
20}
21
22pub enum AutomatonResult<Id, E> {
24 EmptyIter(
28 Id
29 ),
30 CouldNotFindNextState(
32 Id
33 ),
34 Error(
36 E
37 )
38}
39
40impl <Id, E> AutomatonResult<Id, E> {
41 pub fn is_empty_iter(&self) -> bool {
42 return matches!(self, AutomatonResult::EmptyIter(_))
43 }
44
45 pub fn is_could_not_find_next_state(&self) -> bool {
46 return matches!(self, AutomatonResult::CouldNotFindNextState(_))
47 }
48
49 pub fn is_error(&self) -> bool {
50 return matches!(self, AutomatonResult::Error(_))
51 }
52
53 pub fn expect_empty_iter(self) -> Result<Id, AutomatonResult<Id, E>> {
54 if let AutomatonResult::EmptyIter(id) = self {
55 Result::Ok(id)
56 } else {
57 Result::Err(self)
58 }
59 }
60
61 pub fn expect_could_not_find_next_state(self) -> Result<Id, AutomatonResult<Id, E>> {
62 if let AutomatonResult::CouldNotFindNextState(id) = self {
63 Result::Ok(id)
64 } else {
65 Result::Err(self)
66 }
67 }
68
69 pub fn expect_error(&self) -> Result<&E, &AutomatonResult<Id, E>> {
70 if let AutomatonResult::Error(err) = self {
71 Result::Ok(err)
72 } else {
73 Result::Err(self)
74 }
75 }
76}
77
78impl <'a, Id, D, E> Automaton<'a, Id, D, E> {
79 pub fn new_init_fn<FInit: Fn() -> SharedAutomatonState<'a, Id, D, E>>(f_state_graph_init: FInit) -> Self {
81 Self::new(f_state_graph_init())
82 }
83
84 pub fn new(graph_root_state: SharedAutomatonState<'a, Id, D, E>) -> Self {
86 Self {start_state: graph_root_state, _data_phantom: PhantomData{}, _error_phantom: PhantomData{}}
87 }
88
89 pub fn run(&mut self, data: &mut D) -> AutomatonResult<Id, E> {
91 let mut current_state = Rc::clone(&self.start_state);
92 loop {
93 let connection_execute_result = current_state.borrow().execute_next_connection(data);
94 match connection_execute_result {
95 Err(err) => {
96 return AutomatonResult::Error(err);
97 },
98 Ok(next_state_result) => {
99 match next_state_result {
100 NextState::Continue(next_state) => current_state = next_state,
101 NextState::NotFound => return AutomatonResult::CouldNotFindNextState(current_state.borrow().get_id_owned()),
102 NextState::ProcessEnded => return AutomatonResult::EmptyIter(current_state.borrow().get_id_owned()),
103 };
104 },
105 };
106 };
107 }
108}
109
110#[cfg(test)]
111pub mod test {
112 use std::rc::Rc;
113
114 use crate::{automaton::AutomatonResult, automaton_state::{new_shared_automaton_state, AutomatonState, SharedAutomatonState}};
115
116 use super::{Automaton, NextState};
117
118 pub struct TestNodeHello<'a> {
119 next_state: Option<SharedAutomatonState<'a, u8, String, String>>
120 }
121
122 impl<'a> TestNodeHello <'a> {
123 pub fn new(next_state: Option<SharedAutomatonState<'a, u8, String, String>>) -> Self {
124 Self { next_state }
125 }
126 }
127
128 impl <'a> AutomatonState<'a, u8, String, String> for TestNodeHello<'a> {
129 fn get_id_owned(&self) -> u8 {
130 1
131 }
132
133 fn get_id(&self) -> &u8 {
134 &1
135 }
136
137 fn execute_next_connection(&self, data: &mut String) -> Result<NextState<'a, u8, String, String>, String> {
138 data.push_str("Hello");
139 if let Option::Some(nxt_state) = &self.next_state {
140 Result::Ok(NextState::Continue(Rc::clone(nxt_state)))
141 } else {
142 Result::Ok(NextState::NotFound)
143 }
144 }
145 }
146
147 pub struct TestNodeWorld {
148 }
149
150 impl TestNodeWorld {
151 pub fn new() -> Self {
152 Self { }
153 }
154 }
155
156 impl <'a> AutomatonState<'a, u8, String, String> for TestNodeWorld {
157 fn get_id_owned(&self) -> u8 {
158 2
159 }
160
161 fn get_id(&self) -> &u8 {
162 &2
163 }
164
165 fn execute_next_connection(&self, data: &mut String) -> Result<NextState<'a, u8, String, String>, String> {
166 data.push_str(" world");
167 Result::Ok(NextState::ProcessEnded)
168 }
169 }
170
171 #[test]
172 fn automaton_2_nodes_works() -> () {
173 let mut data = String::with_capacity(11);
174 let mut automaton = Automaton::new({
175 let world_state: SharedAutomatonState<u8, String, _> = new_shared_automaton_state(TestNodeWorld::new());
176 let hello_state: SharedAutomatonState<u8, String, _> = new_shared_automaton_state(TestNodeHello::new(Option::Some(Rc::clone(&world_state))));
177 hello_state
178 });
179 let run_res = automaton.run(&mut data);
180 assert!(matches!(run_res, AutomatonResult::EmptyIter(2)));
181 assert_eq!(data, "Hello world");
182 }
183
184 #[test]
185 fn automaton_result_is_empty_iter() -> () {
186 assert!(AutomatonResult::<u8, String>::EmptyIter(1).is_empty_iter());
187 assert!(!AutomatonResult::<u8, String>::CouldNotFindNextState(1).is_empty_iter());
188 assert!(!AutomatonResult::<u8, String>::Error(String::from("Test error")).is_empty_iter());
189 }
190
191 #[test]
192 fn automaton_result_is_could_not_find_next_state() -> () {
193 assert!(!AutomatonResult::<u8, String>::EmptyIter(1).is_could_not_find_next_state());
194 assert!(AutomatonResult::<u8, String>::CouldNotFindNextState(1).is_could_not_find_next_state());
195 assert!(!AutomatonResult::<u8, String>::Error(String::from("Test error")).is_could_not_find_next_state());
196 }
197
198 #[test]
199 fn automaton_result_is_error() -> () {
200 assert!(!AutomatonResult::<u8, String>::EmptyIter(1).is_error());
201 assert!(!AutomatonResult::<u8, String>::CouldNotFindNextState(1).is_error());
202 assert!(AutomatonResult::<u8, String>::Error(String::from("Test error")).is_error());
203 }
204}