pub use rust_automata_macros::state_machine;
pub use rust_automata_macros::Display;
#[doc(hidden)]
#[cfg(feature = "mermaid")]
pub use aquamarine::aquamarine;
pub mod clock;
#[doc(hidden)]
mod takeable;
pub mod timestamp;
use core::fmt::Display;
use std::hash::Hash;
use std::marker::PhantomData;
#[doc(hidden)]
pub use takeable::Takeable;
pub trait Alphabet: Display {
fn nothing() -> Self;
fn any(&self) -> bool;
}
pub trait StateTrait: Display {
fn failure() -> Self;
fn is_failure(&self) -> bool;
}
#[doc(hidden)]
pub trait Enumerable<ForEnum> {
fn enum_id(&self) -> EnumId<ForEnum>;
}
#[doc(hidden)]
pub trait Enumerated<InEnum> {
fn enum_id() -> EnumId<InEnum>;
}
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialOrd, Ord, Default)]
pub struct EnumId<ForEnum> {
pub id: usize,
_marker: PhantomData<ForEnum>,
}
impl<ForEnum> PartialEq for EnumId<ForEnum> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl<ForEnum> Hash for EnumId<ForEnum> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl<ForEnum> EnumId<ForEnum> {
pub fn new(id: usize) -> Self {
EnumId {
id,
_marker: PhantomData,
}
}
}
#[doc(hidden)]
pub trait StateMachineImpl {
type Input: Alphabet;
type State: StateTrait + Enumerable<Self::State>;
type Output: Alphabet;
type InitialState: Enumerated<Self::State> + Into<Self::State>;
fn transition(
&mut self,
state: Takeable<Self::State>,
input: Self::Input,
) -> (Takeable<Self::State>, Self::Output);
fn can_transition(
&self,
state: &Self::State,
input: EnumId<Self::Input>,
) -> Option<EnumId<Self::Output>>;
}
pub struct StateMachine<T: StateMachineImpl> {
state: Takeable<T::State>,
data: T,
}
impl<T> StateMachine<T>
where
T: StateMachineImpl,
{
pub fn new(data: T, initial_state: T::InitialState) -> Self {
Self {
state: Takeable::new(initial_state.into()),
data,
}
}
pub fn step(&mut self) {
let _: T::Output = self.relay::<T::Input, T::Output>(T::Input::nothing());
}
pub fn produce<O: From<T::Output>>(&mut self) -> O {
self.relay::<T::Input, O>(T::Input::nothing())
}
pub fn consume<I: Into<T::Input>>(&mut self, input: I) {
let _: T::Output = self.relay::<I, T::Output>(input);
}
pub fn relay<I: Into<T::Input>, O: From<T::Output>>(&mut self, input: I) -> O {
let enum_input = input.into();
let from_str = self.state.as_ref().to_string();
let input_str = enum_input.to_string();
let current_state = std::mem::replace(&mut self.state, Takeable::new(T::State::failure()));
let (next_state, output) = self.data.transition(current_state, enum_input);
self.state = next_state;
if self.state.is_failure() {
panic!(
"Invalid transition from {} using input {}",
from_str, input_str
);
}
O::from(output)
}
pub fn can_step(&mut self) -> bool {
let enum_input = EnumId::new(0);
let enum_state = self.state.as_ref();
let actual_output = self.data.can_transition(enum_state, enum_input);
actual_output.is_some()
}
pub fn can_produce<O>(&mut self) -> bool
where
O: Enumerated<T::Output>,
{
let enum_input = EnumId::new(0);
let enum_state = self.state.as_ref();
let actual_output = self.data.can_transition(enum_state, enum_input);
let expected_enum = O::enum_id();
match actual_output {
Some(enum_output) => enum_output == expected_enum,
None => false,
}
}
pub fn can_consume<I>(&mut self) -> bool
where
I: Enumerated<T::Input>,
{
let enum_input = I::enum_id();
let enum_state = self.state.as_ref();
let actual_output = self.data.can_transition(enum_state, enum_input);
actual_output.is_some()
}
pub fn can_relay<I, O>(&mut self) -> bool
where
I: Enumerated<T::Input>,
O: Enumerated<T::Output>,
{
let enum_input = I::enum_id();
let enum_state = self.state.as_ref();
let actual_output = self.data.can_transition(enum_state, enum_input);
let expected_enum = O::enum_id();
match actual_output {
Some(enum_output) => enum_output == expected_enum,
None => false,
}
}
pub fn state(&self) -> &T::State {
&self.state
}
pub fn data(&self) -> &T {
&self.data
}
}