#![doc = include_str!("../README.md")]
#![no_std]
pub use edfsm_macros::impl_fsm;
#[derive(Debug, Clone)]
pub enum Input<C, E> {
Command(C),
Event(E),
}
#[derive(Debug, Clone, Copy)]
pub enum Change {
Transitioned,
Updated,
}
pub trait Fsm {
type S;
type C;
type E;
type SE;
fn for_command(s: &Self::S, c: Self::C, se: &mut Self::SE) -> Option<Self::E>;
fn on_event(s: &mut Self::S, e: &Self::E) -> Option<Change>;
fn on_change(s: &Self::S, e: &Self::E, se: &mut Self::SE, change: Change);
fn step(s: &mut Self::S, i: Input<Self::C, Self::E>, se: &mut Self::SE) -> Option<Self::E> {
let e = match i {
Input::Command(c) => Self::for_command(s, c, se),
Input::Event(e) => Some(e),
};
if let Some(e) = e {
let r = Self::on_event(s, &e);
if let Some(c) = r {
Self::on_change(s, &e, se, c);
Some(e)
} else {
None
}
} else {
None
}
}
}
pub trait Init<S> {
fn init(&mut self, state: &S);
}
pub trait Terminating {
fn terminating(&self) -> bool;
}
pub trait Drain {
type Item;
fn drain_all(&mut self) -> impl Iterator<Item = Self::Item> + Send;
}
#[cfg(test)]
mod test {
use super::{Change, Fsm, Input};
#[test]
fn test_step() {
struct Idle;
struct Running;
enum State {
Idle(Idle),
Running(Running),
}
struct Start;
struct Stop;
enum Command {
Start(Start),
Stop(Stop),
}
struct Started;
struct Stopped;
enum Event {
Started(Started),
Stopped(Stopped),
}
struct EffectHandlers {
started: u32,
stopped: u32,
transitioned_stopped_to_started: u32,
}
impl EffectHandlers {
pub fn start_something(&mut self) {
self.started += 1;
}
pub fn stop_something(&mut self) {
self.stopped += 1;
}
pub fn enter_running(&mut self) {
self.transitioned_stopped_to_started += 1;
}
}
struct MyFsm;
impl Fsm for MyFsm {
type S = State;
type C = Command;
type E = Event;
type SE = EffectHandlers;
fn for_command(s: &State, c: Command, se: &mut EffectHandlers) -> Option<Event> {
match (s, c) {
(State::Running(s), Command::Stop(c)) => {
Self::for_running_stop(s, c, se).map(Event::Stopped)
}
(State::Idle(s), Command::Start(c)) => {
Self::for_idle_start(s, c, se).map(Event::Started)
}
_ => None,
}
}
fn on_event(mut s: &mut State, e: &Event) -> Option<Change> {
let r = match (&mut s, e) {
(State::Running(s), Event::Stopped(e)) => Self::on_running_stopped(s, e)
.map(|new_s| (Change::Transitioned, Some(State::Idle(new_s)))),
(State::Idle(s), Event::Started(e)) => Self::on_idle_started(s, e)
.map(|new_s| (Change::Transitioned, Some(State::Running(new_s)))),
_ => None,
};
if let Some((c, new_s)) = r {
if let Some(new_s) = new_s {
*s = new_s;
}
Some(c)
} else {
None
}
}
fn on_change(s: &State, e: &Event, se: &mut EffectHandlers, change: Change) {
if let Change::Transitioned = change {
if let State::Running(s) = s {
Self::on_entry_running(s, e, se)
}
}
match (s, e) {
(State::Idle(s), Event::Stopped(e)) => Self::on_idle_stopped(s, e, se),
(State::Running(s), Event::Started(e)) => Self::on_running_started(s, e, se),
_ => (),
}
}
}
impl MyFsm {
fn on_entry_running(_to_s: &Running, _e: &Event, se: &mut EffectHandlers) {
se.enter_running()
}
fn for_running_stop(
_s: &Running,
_c: Stop,
_se: &mut EffectHandlers,
) -> Option<Stopped> {
Some(Stopped)
}
fn on_running_started(_s: &Running, _e: &Started, se: &mut EffectHandlers) {
se.start_something();
}
fn on_running_stopped(_s: &Running, _e: &Stopped) -> Option<Idle> {
Some(Idle)
}
fn for_idle_start(_s: &Idle, _c: Start, _se: &mut EffectHandlers) -> Option<Started> {
Some(Started)
}
fn on_idle_started(_s: &Idle, _e: &Started) -> Option<Running> {
Some(Running)
}
fn on_idle_stopped(_s: &Idle, _e: &Stopped, se: &mut EffectHandlers) {
se.stop_something();
}
}
let mut se = EffectHandlers {
started: 0,
stopped: 0,
transitioned_stopped_to_started: 0,
};
let e = MyFsm::step(
&mut State::Idle(Idle),
Input::Command(Command::Start(Start)),
&mut se,
);
assert!(matches!(e, Some(Event::Started(Started))));
assert_eq!(se.started, 1);
assert_eq!(se.stopped, 0);
assert_eq!(se.transitioned_stopped_to_started, 1);
let e = MyFsm::step(
&mut State::Running(Running),
Input::Command(Command::Start(Start)),
&mut se,
);
assert!(e.is_none());
assert_eq!(se.started, 1);
assert_eq!(se.stopped, 0);
assert_eq!(se.transitioned_stopped_to_started, 1);
let e = MyFsm::step(
&mut State::Running(Running),
Input::Command(Command::Stop(Stop)),
&mut se,
);
assert!(matches!(e, Some(Event::Stopped(Stopped))));
assert_eq!(se.started, 1);
assert_eq!(se.stopped, 1);
assert_eq!(se.transitioned_stopped_to_started, 1);
let e = MyFsm::step(
&mut State::Idle(Idle),
Input::Command(Command::Stop(Stop)),
&mut se,
);
assert!(e.is_none());
assert_eq!(se.started, 1);
assert_eq!(se.stopped, 1);
assert_eq!(se.transitioned_stopped_to_started, 1);
let mut se = EffectHandlers {
started: 0,
stopped: 0,
transitioned_stopped_to_started: 0,
};
let e = MyFsm::step(
&mut State::Idle(Idle),
Input::Event(Event::Started(Started)),
&mut se,
);
assert!(matches!(e, Some(Event::Started(Started))));
assert_eq!(se.started, 1);
assert_eq!(se.stopped, 0);
assert_eq!(se.transitioned_stopped_to_started, 1);
let e = MyFsm::step(
&mut State::Running(Running),
Input::Event(Event::Started(Started)),
&mut se,
);
assert!(e.is_none());
assert_eq!(se.started, 1);
assert_eq!(se.stopped, 0);
assert_eq!(se.transitioned_stopped_to_started, 1);
let e = MyFsm::step(
&mut State::Running(Running),
Input::Event(Event::Stopped(Stopped)),
&mut se,
);
assert!(matches!(e, Some(Event::Stopped(Stopped))));
assert_eq!(se.started, 1);
assert_eq!(se.stopped, 1);
assert_eq!(se.transitioned_stopped_to_started, 1);
let e = MyFsm::step(
&mut State::Idle(Idle),
Input::Event(Event::Stopped(Stopped)),
&mut se,
);
assert!(e.is_none());
assert_eq!(se.started, 1);
assert_eq!(se.stopped, 1);
assert_eq!(se.transitioned_stopped_to_started, 1);
}
}