use std::{
error::Error,
fmt::{Debug, Display, Formatter},
};
pub trait StateMachine: Sized {
type Error: Error;
type State;
type SharedState;
type Event;
type Command;
fn on_event(self, event: Self::Event) -> TransitionResult<Self, Self::State>;
fn on_event_mut(
&mut self,
event: Self::Event,
) -> Result<Vec<Self::Command>, MachineError<Self::Error>>
where
Self: Clone,
{
let res = self.clone().on_event(event);
match res {
TransitionResult::Ok {
commands,
new_state,
shared_state,
} => {
*self = Self::from_parts(shared_state, new_state);
Ok(commands)
}
TransitionResult::OkNoShare {
commands,
new_state,
} => {
self.set_state(new_state);
Ok(commands)
}
TransitionResult::InvalidTransition => Err(MachineError::InvalidTransition),
TransitionResult::Err(e) => Err(MachineError::Underlying(e)),
}
}
fn name(&self) -> &str;
fn state(&self) -> &Self::State;
fn set_state(&mut self, new_state: Self::State);
fn shared_state(&self) -> &Self::SharedState;
fn on_final_state(&self) -> bool;
fn from_parts(shared: Self::SharedState, state: Self::State) -> Self;
fn visualizer() -> &'static str;
}
#[derive(Debug)]
pub enum MachineError<E: Error> {
InvalidTransition,
Underlying(E),
}
impl<E: Error> Display for MachineError<E> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
MachineError::InvalidTransition => f.write_str("Invalid transition"),
MachineError::Underlying(e) => Display::fmt(&e, f),
}
}
}
impl<E: Error> Error for MachineError<E> {}
pub enum MachineUpdate<Machine>
where
Machine: StateMachine,
{
InvalidTransition,
Ok { commands: Vec<Machine::Command> },
}
impl<M> MachineUpdate<M>
where
M: StateMachine,
{
pub fn unwrap(self) -> Vec<M::Command> {
match self {
Self::Ok { commands } => commands,
_ => panic!("Transition was not successful!"),
}
}
}
pub enum TransitionResult<Machine, DestinationState>
where
Machine: StateMachine,
DestinationState: Into<Machine::State>,
{
InvalidTransition,
Ok {
commands: Vec<Machine::Command>,
new_state: DestinationState,
shared_state: Machine::SharedState,
},
OkNoShare {
commands: Vec<Machine::Command>,
new_state: DestinationState,
},
Err(Machine::Error),
}
impl<Sm, Ds> TransitionResult<Sm, Ds>
where
Sm: StateMachine,
Ds: Into<Sm::State>,
{
pub fn ok<CI>(commands: CI, new_state: Ds) -> Self
where
CI: IntoIterator<Item = Sm::Command>,
{
Self::OkNoShare {
commands: commands.into_iter().collect(),
new_state,
}
}
pub fn ok_shared<CI, SS>(commands: CI, new_state: Ds, new_shared: SS) -> Self
where
CI: IntoIterator<Item = Sm::Command>,
SS: Into<Sm::SharedState>,
{
Self::Ok {
commands: commands.into_iter().collect(),
new_state,
shared_state: new_shared.into(),
}
}
pub fn from<CurrentState>(current_state: CurrentState) -> Self
where
CurrentState: Into<Ds>,
{
let as_dest: Ds = current_state.into();
Self::OkNoShare {
commands: vec![],
new_state: as_dest,
}
}
}
impl<Sm, Ds> TransitionResult<Sm, Ds>
where
Sm: StateMachine,
Ds: Into<Sm::State> + Default,
{
pub fn commands<CI>(commands: CI) -> Self
where
CI: IntoIterator<Item = Sm::Command>,
{
Self::OkNoShare {
commands: commands.into_iter().collect(),
new_state: Ds::default(),
}
}
pub fn default() -> Self {
Self::OkNoShare {
commands: vec![],
new_state: Ds::default(),
}
}
}
impl<Sm, Ds> TransitionResult<Sm, Ds>
where
Sm: StateMachine,
Ds: Into<Sm::State>,
{
pub fn into_general(self) -> TransitionResult<Sm, Sm::State> {
match self {
TransitionResult::InvalidTransition => TransitionResult::InvalidTransition,
TransitionResult::Ok {
commands,
new_state,
shared_state,
} => TransitionResult::Ok {
commands,
new_state: new_state.into(),
shared_state,
},
TransitionResult::OkNoShare {
commands,
new_state,
} => TransitionResult::OkNoShare {
commands,
new_state: new_state.into(),
},
TransitionResult::Err(e) => TransitionResult::Err(e),
}
}
}