use std::{
fmt::{Debug, Display},
hash::{Hash, Hasher},
marker::PhantomData,
};
use derive_builder::Builder;
use derive_getters::Getters;
use thiserror::Error;
pub type Mutation<'a, C> = fn(&mut C);
pub type BoxedMutation<'a, C> = Box<Mutation<'a, C>>;
pub trait StateLike: Clone + Hash + PartialEq + Eq {}
impl<T> StateLike for T where T: Clone + Hash + PartialEq + Eq {}
pub trait ToPrimitive
where
Self::Output: Copy + Hash + PartialEq + Eq + Debug,
{
type Output;
fn to_primitive(&self) -> Self::Output;
}
pub trait FromPrimitive: Sized + ToPrimitive {
fn from_primitive(primitive: Self::Output) -> Option<Self>;
}
macro_rules! impl_trivial_primitives {
($($t:ty),*) => {
$(
impl ToPrimitive for $t {
type Output = Self;
fn to_primitive(&self) -> Self::Output {
*self
}
}
impl FromPrimitive for $t {
fn from_primitive(primitive: Self::Output) -> Option<Self> {
Some(primitive)
}
}
)*
};
}
impl_trivial_primitives! { u8, u16, u32, u64, i8, i16, i32, i64, isize, usize }
#[derive(Error, Debug)]
pub enum Error<'a, S, I> {
NoInitialState,
TransitionImpossible((&'a S, I)),
NoHistory,
UnknownState,
}
impl<S: Display, I: Display> Display for Error<'_, S, I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::TransitionImpossible((from, event)) => {
write!(f, "Cannot transition from {} with event {}", from, event)
}
Error::NoHistory => write!(f, "History entry is missing"),
Error::UnknownState => write!(
f,
"A state is used without being defined in the state machine"
),
Error::NoInitialState => write!(f, "Machine has no initial state defined"),
}
}
}
pub trait ConfigurableStateMachine<'a>
where
Self::StateMachine: RunningStateMachine<'a>,
{
type Input;
type State;
type Context;
type Output;
type StateMachine;
fn start(self) -> Result<Self::StateMachine, Error<'a, Self::State, Self::Input>>;
fn set_initial_state(&mut self, initial_state: Self::State);
fn define_state(
&mut self,
state: Self::State,
options: state::Options<'a, Self>,
) -> Option<Self::State>;
fn define_state_with_parent(
&mut self,
state: Self::State,
parent: Self::State,
) -> Option<Self::State> {
self.define_state(
state,
state::OptionsBuilder::default()
.parent(Some(parent))
.build()
.unwrap(),
)
}
fn add_transition(
&mut self,
source_state: Self::State,
event: Self::Input,
target_state: Self::State,
output: Option<Self::Output>,
mutation: Option<fn(&mut Self::Context)>,
);
fn add_transition_with_mutation(
&mut self,
source_state: Self::State,
event: Self::Input,
target_state: Self::State,
output: Option<Self::Output>,
mutation: fn(&mut Self::Context),
) {
self.add_transition(source_state, event, target_state, output, Some(mutation));
}
fn add_transition_immutable(
&mut self,
source_state: Self::State,
event: Self::Input,
target_state: Self::State,
output: Option<Self::Output>,
) {
self.add_transition(source_state, event, target_state, output, None);
}
}
pub mod state {
use super::*;
#[derive(Builder, Getters, Debug, Clone)]
#[builder(pattern = "owned")]
pub struct Options<'a, T: ?Sized + ConfigurableStateMachine<'a>> {
#[builder(default = false)]
pub(crate) is_virtual: bool,
#[builder(default = None)]
pub(crate) parent: Option<T::State>,
#[builder(default = None)]
pub(crate) on_enter: Option<Mutation<'a, T::Context>>,
#[builder(default = None)]
pub(crate) on_leave: Option<Mutation<'a, T::Context>>,
}
}
pub trait RunningStateMachine<'a> {
type Input;
type State;
type Context;
type Output;
fn states(&self) -> Vec<&Self::State>;
fn state(&self) -> &Self::State;
fn context(&self) -> &Self::Context;
fn is_final(&self) -> bool;
fn is_active(&self, state: Self::State) -> bool;
#[allow(clippy::type_complexity)]
fn consume(
&mut self,
event: Self::Input,
) -> Result<Option<&Self::Output>, Error<'_, Self::State, Self::Input>>;
}
#[allow(dead_code)]
pub(crate) trait StateMachineMut<'a>: RunningStateMachine<'a> {
fn set_state(&mut self, value: Self::State);
fn context_mut(&mut self) -> &mut Self::Context;
}
#[derive(Debug, Clone)]
pub enum StateMachineEvent<S> {
StateChanged((S, S)),
}
#[cfg(feature = "events")]
pub trait EventProducer<S> {
fn emit(
&self,
event: StateMachineEvent<S>,
) -> Result<(), crossbeam_channel::SendError<StateMachineEvent<S>>>;
}
pub trait History<'a>: RunningStateMachine<'a> {
type Len;
fn history_len(&self) -> Self::Len;
fn rewind(&mut self) -> Result<(), Error<'_, Self::State, Self::Input>>;
}
pub trait StateDefinition<'a, S, C> {
fn value(&self) -> &S;
fn on_enter(&self) -> Option<&Mutation<'a, C>>;
fn on_leave(&self) -> Option<&Mutation<'a, C>>;
}
#[derive(Getters, Builder)]
pub struct StateDefWrapper<'a, S, C> {
#[getter(skip)]
marker: PhantomData<&'a mut C>,
value: S,
on_enter: Option<Mutation<'a, C>>,
on_leave: Option<Mutation<'a, C>>,
is_virtual: bool,
}
impl<'a, S, C> StateDefinition<'a, S, C> for StateDefWrapper<'a, S, C> {
fn value(&self) -> &S {
&self.value
}
fn on_enter(&self) -> Option<&Mutation<'a, C>> {
self.on_enter.as_ref()
}
fn on_leave(&self) -> Option<&Mutation<'a, C>> {
self.on_leave.as_ref()
}
}
impl<S, C> Debug for StateDefWrapper<'_, S, C>
where
S: Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("StateWrapper")
.field("marker", &self.marker)
.field("value", &self.value)
.field("on_enter", &self.on_enter.as_ref().map(|_| "{function}"))
.field("on_leave", &self.on_leave.as_ref().map(|_| "{function}"))
.finish()
}
}
impl<S, C> Clone for StateDefWrapper<'_, S, C>
where
S: Clone,
{
fn clone(&self) -> Self {
Self {
marker: self.marker,
value: self.value.clone(),
on_enter: self.on_enter,
on_leave: self.on_leave,
is_virtual: false,
}
}
}
impl<S, C> Hash for StateDefWrapper<'_, S, C>
where
S: Hash,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.value.hash(state);
}
}
impl<S, C> PartialEq for StateDefWrapper<'_, S, C>
where
S: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<S, C> Eq for StateDefWrapper<'_, S, C> where S: PartialEq {}
impl<'a, S, C> StateDefWrapper<'a, S, C> {
pub fn new(value: S) -> Self {
Self {
marker: PhantomData,
value,
on_enter: None,
on_leave: None,
is_virtual: false,
}
}
pub fn with_mutations(
value: S,
on_enter: Option<Mutation<'a, C>>,
on_leave: Option<Mutation<'a, C>>,
) -> Self {
Self {
marker: PhantomData,
value,
on_enter,
on_leave,
is_virtual: false,
}
}
}
impl<S, C> AsRef<S> for StateDefWrapper<'_, S, C> {
fn as_ref(&self) -> &S {
self.value()
}
}
impl<S, C> ToPrimitive for StateDefWrapper<'_, S, C>
where
S: ToPrimitive,
{
type Output = <S as ToPrimitive>::Output;
fn to_primitive(&self) -> Self::Output {
self.value().to_primitive()
}
}
impl<S, C> FromPrimitive for StateDefWrapper<'_, S, C>
where
S: FromPrimitive,
{
fn from_primitive(primitive: Self::Output) -> Option<Self> {
S::from_primitive(primitive).map(StateDefWrapper::new)
}
}