pub type Action<'a, S, E> = Box<Fn(&S,&E) + 'a>;
pub type Predicate<'a, S, E> = Box<Fn(&S,&E) -> bool + 'a>;
pub trait EnumTag: Copy {
fn tag_number(&self) -> usize;
fn max_tag_number() -> usize;
}
struct Transition<'a, S: EnumTag, E: EnumTag> {
next_state: S,
action: Action<'a, S, E>,
}
struct StateTransitions<'a, S: EnumTag, E: EnumTag> {
edges: Vec<Option<Transition<'a, S, E>>>,
}
pub struct Machine<'a, S: EnumTag, E: EnumTag> {
state: S,
transitions: Vec<StateTransitions<'a, S, E>>,
}
impl<'a, S: EnumTag, E: EnumTag> Machine<'a, S, E> {
pub fn new(initial_state: S) -> Machine<'a, S, E> {
let mut transitions = Vec::with_capacity(S::max_tag_number());
for _ in 0..S::max_tag_number() + 1 {
let mut edges = Vec::with_capacity(E::max_tag_number());
for _ in 0..E::max_tag_number() + 1 {
edges.push(None);
}
transitions.push(StateTransitions {
edges: edges,
});
}
Machine {
state: initial_state,
transitions: transitions,
}
}
pub fn add_transition<F>(&mut self, in_state: S, on_event: E, next_state: S, action: F) -> bool
where F: Fn(&S, &E) + 'a{
let transition = &mut self.transitions[in_state.tag_number()];
let edge = &mut transition.edges[on_event.tag_number()];
if edge.is_none() {
*edge = Some(Transition {
action: Box::new(action),
next_state: next_state,
});
true
} else {
false
}
}
pub fn current_state(&self) -> S {
self.state
}
pub fn on_event(&mut self, event_type: E) {
let transition = &self.transitions[self.state.tag_number()];
let edge = &transition.edges[event_type.tag_number()];
if let &Some(ref t) = edge {
(*t.action)(&self.state, &event_type);
self.state = t.next_state;
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum TurnStyleState {
Locked,
Unlocked,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum TurnStyleEvent {
Push,
InsertCoin,
}
impl EnumTag for TurnStyleState {
fn tag_number(&self) -> usize {
*self as usize
}
fn max_tag_number() -> usize {
TurnStyleState::Unlocked as usize
}
}
impl EnumTag for TurnStyleEvent {
fn tag_number(&self) -> usize {
*self as usize
}
fn max_tag_number() -> usize {
TurnStyleEvent::InsertCoin as usize
}
}
#[test]
fn test_machine() {
let mut machine = Machine::new(TurnStyleState::Locked);
machine.add_transition(
TurnStyleState::Locked, TurnStyleEvent::InsertCoin,
TurnStyleState::Unlocked, |_,_| println!("unlocked")
);
machine.add_transition(
TurnStyleState::Unlocked, TurnStyleEvent::Push,
TurnStyleState::Locked, |_,_| println!("locked")
);
assert!(machine.current_state() == TurnStyleState::Locked);
machine.on_event(TurnStyleEvent::Push);
assert!(machine.current_state() == TurnStyleState::Locked);
machine.on_event(TurnStyleEvent::InsertCoin);
assert!(machine.current_state() == TurnStyleState::Unlocked);
machine.on_event(TurnStyleEvent::InsertCoin);
assert!(machine.current_state() == TurnStyleState::Unlocked);
machine.on_event(TurnStyleEvent::Push);
assert!(machine.current_state() == TurnStyleState::Locked);
}
}