use crate::error::RestoreError;
use crate::error::TransitionError;
use crate::kind::{self, Kind};
use crate::listener::{
Clock, RejectionListener, TransitionListener, TransitionRecord, default_clock,
};
use crate::metadata::{self, MachineMetadata};
use crate::migrate::Versioned;
use core::fmt;
use std::time::Instant;
pub trait TransitionRules: Sized {
type Event: EventKind + Clone + core::fmt::Debug;
fn transition(&self, event: &Self::Event) -> Option<Self>;
}
pub trait EventKind: Sized {
fn kinds(&self) -> Option<&'static [Kind]>;
fn variant_name(&self) -> &'static str;
fn event_variants() -> Vec<Self>;
fn likelihood(&self) -> f64 {
0.35
}
}
pub trait StateMachine: TransitionRules + Clone + fmt::Debug + PartialEq + Sized {
fn initial() -> Self;
fn is_terminal(&self) -> bool;
fn restriction(&self) -> Option<&'static [Kind]>;
fn state_variants() -> Vec<Self>;
fn variant_name(&self) -> &'static str;
}
pub struct Machine<S: StateMachine>
where
S::Event: EventKind,
{
state: S,
on_transition: Vec<TransitionListener<S>>,
on_rejection: Vec<RejectionListener<S>>,
clock: Clock,
}
impl<S: StateMachine> Machine<S>
where
S::Event: EventKind,
{
pub fn new() -> Self {
Self {
state: S::initial(),
on_transition: Vec::new(),
on_rejection: Vec::new(),
clock: default_clock(),
}
}
pub fn restore(state: S) -> Self {
Self {
state,
on_transition: Vec::new(),
on_rejection: Vec::new(),
clock: default_clock(),
}
}
pub fn restore_versioned(bytes: &[u8]) -> Result<Self, RestoreError>
where
S: Versioned,
{
Ok(Self::restore(S::restore_versioned(bytes)?))
}
pub fn state(&self) -> &S {
&self.state
}
pub fn apply(&mut self, event: S::Event) -> Result<S, TransitionError<S, S::Event>> {
if self.state.is_terminal() {
let err = TransitionError::TerminalState {
state: self.state.clone(),
event,
};
self.fire_rejection(&err);
return Err(err);
}
if let Some(expected) = self.state.restriction() {
let event_kind = event.kinds();
let accepted = matches!(event_kind, Some(ek) if kind::intersects(expected, ek));
if !accepted {
let err = TransitionError::EventKindRejected {
state: self.state.clone(),
event,
expected_kinds: expected,
event_kind,
};
self.fire_rejection(&err);
return Err(err);
}
}
match self.state.transition(&event) {
Some(next) => {
let from = self.state.clone();
self.state = next.clone();
let record = TransitionRecord {
from_state: from,
event,
to_state: next.clone(),
timestamp: (self.clock)(),
};
self.fire_transition(&record);
Ok(next)
}
None => {
let err = TransitionError::NoTransition {
state: self.state.clone(),
event,
};
self.fire_rejection(&err);
Err(err)
}
}
}
pub fn could_apply(&self, event: &S::Event) -> bool {
if self.state.is_terminal() {
return false;
}
if let Some(expected) = self.state.restriction()
&& !matches!(event.kinds(), Some(ek) if kind::intersects(expected, ek))
{
return false;
}
self.state.transition(event).is_some()
}
pub fn why_not(&self, event: &S::Event) -> Option<TransitionError<S, S::Event>> {
if self.state.is_terminal() {
return Some(TransitionError::TerminalState {
state: self.state.clone(),
event: event.clone(),
});
}
if let Some(expected) = self.state.restriction() {
let event_kind = event.kinds();
if !matches!(event_kind, Some(ek) if kind::intersects(expected, ek)) {
return Some(TransitionError::EventKindRejected {
state: self.state.clone(),
event: event.clone(),
expected_kinds: expected,
event_kind,
});
}
}
if self.state.transition(event).is_none() {
return Some(TransitionError::NoTransition {
state: self.state.clone(),
event: event.clone(),
});
}
None
}
pub fn peek_transition(&self, event: &S::Event) -> Option<S> {
if !self.could_apply(event) {
return None;
}
self.state.transition(event)
}
pub fn metadata() -> MachineMetadata<S> {
metadata::build::<S>()
}
pub fn on_transition(
&mut self,
listener: impl Fn(&TransitionRecord<S, S::Event>) + 'static,
) -> &mut Self {
self.on_transition.push(Box::new(listener));
self
}
pub fn on_rejection(
&mut self,
listener: impl Fn(&TransitionError<S, S::Event>) + 'static,
) -> &mut Self {
self.on_rejection.push(Box::new(listener));
self
}
pub fn set_clock(&mut self, clock: impl Fn() -> Instant + 'static) -> &mut Self {
self.clock = Box::new(clock);
self
}
fn fire_transition(&self, record: &TransitionRecord<S, S::Event>) {
for listener in &self.on_transition {
listener(record);
}
}
fn fire_rejection(&self, error: &TransitionError<S, S::Event>) {
for listener in &self.on_rejection {
listener(error);
}
}
}
impl<S: StateMachine> Default for Machine<S>
where
S::Event: EventKind,
{
fn default() -> Self {
Self::new()
}
}
impl<S: StateMachine + fmt::Debug> fmt::Debug for Machine<S>
where
S::Event: EventKind,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Machine")
.field("state", &self.state)
.finish_non_exhaustive()
}
}