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
use crate::core::transition::Transition;
use crate::core::transitionable::Transitionable;
use crate::core::errors::*;
use std::fmt::Debug;
/// Defines the Machine.
#[derive(Debug, Default)]
pub struct Machine<S, E>
where
S: Eq + Clone + Debug, // Type of the State(s)
E: Eq + Clone + Debug, // Type of the Event(s)
{
/// The transitions of the machine.
pub transitions: Vec<Transition<S, E>>,
}
impl<S, E> Machine<S, E>
where
S: Eq + Clone + Copy + Debug,
E: Eq + Clone + Copy + Debug,
{
/// Creates an empty machine.
pub fn new() -> Machine<S, E> {
Self {
transitions: vec![],
}
}
/// Registers a new [Transition] in the [Machine].
///
/// # Panics
///
/// Panics if the given [Transition]:
/// * is already present in the [Machine]
/// * has the same input and event as another, preventing to decide which output is selected
pub fn add_transition(&mut self, transition: Transition<S, E>) {
if self.transitions.contains(&transition) {
panic!("{}", TransitionError::already_exists(transition.state_in, transition.event, transition.state_out))
} else if self.transitions.iter().any(|trans| trans.partial_compare(Some(&transition.state_in), Some(&transition.event), None)) {
panic!("{}", TransitionError::nondeterministic(transition.state_in, transition.event, transition.state_out))
} else {
self.transitions.push(transition);
}
}
/// Returns the [String] `output_state` for the given `input_state` and `event` based on the [Transition]s
/// registered in the [Machine].
///
/// # Errors
///
/// Errors if `event` cannot be aplied on `input_state` (no matching transition).
pub fn get_output(&self, input_state: &S, event: &E) -> Result<&S, TransitionError<S, E>> {
for transition in &self.transitions {
if transition.state_in == *input_state && transition.event == *event {
if transition.is_allowed() {
return Ok(&transition.state_out);
} else {
return Err(TransitionError::not_allowed(transition.state_in, transition.event, transition.state_out))
}
}
}
Err(TransitionError::cannot_apply(*input_state, *event))
}
/// Returns the [String] `output_state` for the given:
/// * `object` - a [Transitionable] with a current state
/// * `event` - an event to apply
///
/// The new (output) state will be set in `object`.
///
/// # Errors
///
/// Errors if `event` cannot be applied on the current state of `object`
pub fn apply(&self, object: &mut impl Transitionable<S>, event: E) -> Result<&S, TransitionError<S, E>> {
let output = self.get_output(&object.get_state(), &event);
match output {
Ok(state) => {
object.set_state(*state);
Ok(state)
},
Err(error) => Err(error),
}
}
}