automata_like_programming/simple_impl/
simple_state.rs

1use std::{cell::RefCell, marker::PhantomData, rc::Rc};
2
3use crate::automaton_state::{convert_to_dyn_reference, AutomatonState, SharedAutomatonState};
4
5pub type SharedSimpleState<'a, K, Id, D, E> = Rc<RefCell<SimpleStateImplementation<'a, K, Id, D, E>>>;
6
7/// Represents data, that can provide a key which will be used while searching for next state. Usually will use iterator
8/// based on a sequence.
9pub trait KeyProvidingData<K> {
10    fn next_key(&mut self) -> Option<K>;
11}
12
13///
14/// Connection representing edge between two nodes (or one node with itself) in a graph structure. Matcher is used to
15/// find the next state. Based on the key provided by the data. Each connection has a specified function which will be 
16/// executed while changing to matched next state.
17/// 
18/// * `matcher` - Defines whether this connection should be chosen for a specified key. It's up to the user to ensure
19/// that connections don't have intersecting matchers. The first connection matched for a key will always be used.
20/// * `exec_function` - Operation that will be executing while changing state.
21/// * `connected_state` - State that will be returned if this connection is matched. Can be the same state that this
22/// connection will be assigned to.
23pub struct SimpleInterStateConnection<'a, K, Id, D, E> where Id: Copy + 'a, K: 'a, D: 'a, E: 'a {
24    matcher: Box<dyn Fn(&K) -> bool + 'a>,
25    exec_function: Box<dyn Fn(&mut D, &K) -> Result<(), E> + 'a>,
26    connected_state: SharedAutomatonState<'a, Id, D, E>,
27}
28
29impl <'a, K, Id, D, E> SimpleInterStateConnection<'a, K, Id, D, E> where Id: Copy {
30    /// Creates new connection with specified matcher and a procedure that will be executed when this connection is matched.
31    pub fn new<M: Fn(&K) -> bool + 'a, FExec: Fn(&mut D, &K) -> Result<(), E> + 'a, S: AutomatonState<'a, Id, D, E> + 'a>(matcher: M, exec_function: FExec, next_state: &Rc<RefCell<S>>) -> Self {
32        Self { matcher: Box::new(matcher), exec_function: Box::new(exec_function), connected_state: convert_to_dyn_reference(Rc::clone(next_state)) }
33    }
34
35    /// Creates new connection with specified matcher. Does nothing when matched (designed to be used with intermediate states).
36    pub fn new_no_action<M: Fn(&K) -> bool + 'a, S: AutomatonState<'a, Id, D, E> + 'a>(matcher: M, next_state: &Rc<RefCell<S>>) -> Self {
37        Self::new(matcher, Self::do_nothing, next_state)
38    }
39
40    /// Creates new connection which will always be matched and will execute function. Used for default connections.
41    pub fn new_always_matched<S: AutomatonState<'a, Id, D, E> + 'a, FExec: Fn(&mut D, &K) -> Result<(), E> + 'a>(exec_function: FExec, next_state: &Rc<RefCell<S>>) -> Self {
42        Self::new(Self::always_match, exec_function, next_state)
43    }
44
45    /// Creates new connection which will always be matched and will do nothing. Used for default connections.
46    pub fn new_no_action_always_matched<S: AutomatonState<'a, Id, D, E> + 'a>(next_state: &Rc<RefCell<S>>) -> Self {
47        Self::new_always_matched(Self::do_nothing,next_state)
48    }
49
50    /// Always matches.
51    fn always_match(_: &K) -> bool {
52        true
53    }
54
55    /// Does nothing.
56    fn do_nothing(_:&mut D, _:&K) -> Result<(), E> {
57        Result::Ok(())
58    }
59}
60
61/// AutomatonState implementating struct which simplifies state definition by managing list of defined connections. 
62/// Depends on data for providing next key. This key is then used to match a connection from the defined list.
63/// Each state has an assigned identifier which is used to inform which state did the automaton stop on.
64/// Identifier is copied to the result meaning it has to implement the *Copy* trait.
65pub struct SimpleStateImplementation<'a, K, Id, D, E> where D: KeyProvidingData<K>, Id: Copy{
66    _phantom: PhantomData<D>,
67    id: Id,
68    next_states: Vec<SimpleInterStateConnection<'a, K, Id, D, E>>,
69}
70
71impl <'a, K, Id, D, E> SimpleStateImplementation<'a, K, Id, D, E> where D: KeyProvidingData<K>, Id: Copy {
72    /// Creates new simple state with provided identifier.
73    /// 
74    /// * `id` - Identifier of this state which will be copied into result when automaton stops on this state.
75    pub fn new(id: Id) -> Self {
76        Self { _phantom: PhantomData{}, next_states: Vec::new(), id}
77    }
78
79    /// Adds connection to possible next states of current state.
80    pub fn register_connection(&mut self, connection: SimpleInterStateConnection<'a, K, Id, D, E>) -> () 
81    {
82        self.next_states.push(connection);
83    }
84}
85
86impl<'a, K, Id, D, E> AutomatonState<'a, Id, D, E> for SimpleStateImplementation<'a, K, Id, D, E> where D: KeyProvidingData<K>, Id: Copy {
87    /// Returns owned copy of identifier of this state.
88    fn get_id_owned(&self) -> Id {
89        self.id
90    }
91
92    /// Returns identifier of this state.
93    fn get_id(&self) -> &Id {
94        &self.id
95    }
96
97    /// Finds connection by popping key from key iterator. Executes assigned function and returns next state if everything goes
98    /// alright. 
99    fn execute_next_connection(&self, data: &mut D) -> Result<crate::automaton::NextState<'a, Id, D, E>, E> {
100        let next_key = data.next_key();
101        if let Option::Some(k) = next_key {
102            for c in &self.next_states {
103                if (c.matcher)(&k) {
104                    (c.exec_function)(data, &k)?;
105                    return Result::Ok(crate::automaton::NextState::Continue(Rc::clone(&c.connected_state)));
106                }
107            }
108            Result::Ok(crate::automaton::NextState::NotFound)
109        } else {
110            Result::Ok(crate::automaton::NextState::ProcessEnded)
111        }
112    }
113}
114
115#[cfg(test)]
116mod test {
117    use super::KeyProvidingData;
118
119    struct TestData {
120        buffer: String,
121        end: u8,
122        current: u8,
123    }
124
125    impl TestData {
126        pub fn new(start: u8, end: u8) -> Self {
127            Self { buffer: String::new(), end, current: start }
128        }
129
130        pub fn append_text(&mut self, text: &str) -> () {
131            self.buffer.push_str(text);
132        }
133
134        pub fn data(&self) -> &String {
135            &self.buffer
136        }
137    }
138
139    impl KeyProvidingData<u8> for TestData {
140        fn next_key(&mut self) -> Option<u8> {
141            if self.current >= self.end {
142                return Option::None
143            }
144            let res = Option::Some(self.current);
145            self.current += 1;
146            return res;
147        }
148    }
149
150    mod automaton_test {
151        use crate::{automaton::{Automaton, AutomatonResult}, automaton_state::new_shared_concrete_state, simple_impl::simple_state::{test::TestData, SimpleInterStateConnection, SimpleStateImplementation}};
152
153        #[test]
154        fn automaton_with_simple_states_works() -> () {
155            let mut data = TestData::new(1, 4);
156            let mut automaton = Automaton::new({
157                let world_state = new_shared_concrete_state(SimpleStateImplementation::new(3));
158                let simple_state = new_shared_concrete_state(SimpleStateImplementation::new(2));
159                simple_state.borrow_mut().register_connection(SimpleInterStateConnection::new(|k| k == &2, |d: &mut TestData, _| {
160                    d.append_text(" simple ");
161                    let res: Result<(), String> = Result::Ok(());
162                    res
163                }, &world_state));
164                let hello_state = new_shared_concrete_state(SimpleStateImplementation::new(1));
165                hello_state.borrow_mut().register_connection(SimpleInterStateConnection::new(|k| k == &1, |d: &mut TestData, _| {
166                    d.append_text("Hello");
167                    Result::Ok(())
168                }, &simple_state));
169                world_state.borrow_mut().register_connection(SimpleInterStateConnection::new(|k| k == &3, |d: &mut TestData, _| {
170                    d.append_text("world!");
171                    Result::Ok(())
172                }, &hello_state));
173                hello_state
174            });
175            let run_result = automaton.run(&mut data);
176            assert_eq!(data.data(), "Hello simple world!");
177            assert!(matches!(run_result, AutomatonResult::EmptyIter(1)));
178        }
179
180        // TBF I don't know if this situation should be Ok or Err
181        #[test]
182        fn automaton_with_simple_states_works_no_next_state_found() -> () {
183            let mut data = TestData::new(2, 3);
184            let mut automaton = Automaton::new(new_shared_concrete_state(SimpleStateImplementation::new(1)));
185            let run_result: AutomatonResult<u32, String> = automaton.run(&mut data);
186            assert_eq!(data.data(), "");
187            assert!(matches!(run_result, AutomatonResult::CouldNotFindNextState(1)));
188        }
189    }
190}