#![deny(missing_docs)]
pub enum StateTransition<S> {
None,
Pop,
Push(Box<dyn State<S>>),
Switch(Box<dyn State<S>>),
Quit,
}
pub trait State<S> {
fn on_start(&mut self, _state_data: &mut S) {}
fn on_stop(&mut self, _state_data: &mut S) {}
fn on_pause(&mut self, _state_data: &mut S) {}
fn on_resume(&mut self, _state_data: &mut S) {}
fn update(&mut self, _state_data: &mut S) -> StateTransition<S> {
StateTransition::None
}
}
pub struct StateMachine<S> {
state_stack: Vec<Box<dyn State<S>>>,
}
impl<S> Default for StateMachine<S> {
fn default() -> Self {
Self {
state_stack: Vec::default(),
}
}
}
impl<S> StateMachine<S> {
pub fn is_running(&self) -> bool {
!self.state_stack.is_empty()
}
pub fn update(&mut self, state_data: &mut S) {
let trans = match self.state_stack.last_mut() {
Some(state) => state.update(state_data),
None => StateTransition::None,
};
self.transition(trans, state_data);
}
fn transition(&mut self, request: StateTransition<S>, state_data: &mut S) {
match request {
StateTransition::None => (),
StateTransition::Pop => self.pop(state_data),
StateTransition::Push(state) => self.push(state, state_data),
StateTransition::Switch(state) => self.switch(state, state_data),
StateTransition::Quit => self.stop(state_data),
}
}
fn switch(&mut self, mut state: Box<dyn State<S>>, state_data: &mut S) {
if let Some(mut state) = self.state_stack.pop() {
state.on_stop(state_data)
}
state.on_start(state_data);
self.state_stack.push(state);
}
pub fn push(&mut self, mut state: Box<dyn State<S>>, state_data: &mut S) {
if let Some(state) = self.state_stack.last_mut() {
state.on_pause(state_data);
}
state.on_start(state_data);
self.state_stack.push(state);
}
fn pop(&mut self, state_data: &mut S) {
if let Some(mut state) = self.state_stack.pop() {
state.on_stop(state_data);
}
if let Some(state) = self.state_stack.last_mut() {
state.on_resume(state_data);
}
}
pub fn stop(&mut self, state_data: &mut S) {
while let Some(mut state) = self.state_stack.pop() {
state.on_stop(state_data);
}
}
}
#[cfg(test)]
mod tests {
use crate::*;
type StateData = (isize, isize);
pub struct Test;
impl State<StateData> for Test {
fn on_start(&mut self, data: &mut StateData) {
data.0 += data.1;
}
fn on_resume(&mut self, data: &mut StateData) {
self.on_start(data);
}
fn update(&mut self, _data: &mut StateData) -> StateTransition<StateData> {
StateTransition::Push(Box::new(Test))
}
}
#[test]
fn sm_test() {
let mut sm = StateMachine::<StateData>::default();
let mut state_data = (0, 10);
sm.push(Box::new(Test), &mut state_data);
assert!(state_data.0 == 10);
sm.update(&mut state_data);
assert!(state_data.0 == 20);
sm.stop(&mut state_data);
assert!(state_data.0 == 20);
assert!(!sm.is_running())
}
}