use core::cmp::Ordering;
use crate::blocking::IntoStateMachine;
use crate::{Outcome, StateOrSuperstate};
pub trait Superstate<M>
where
M: IntoStateMachine,
{
fn call_handler(
&mut self,
shared_storage: &mut M,
event: &M::Event<'_>,
context: &mut M::Context<'_>,
) -> Outcome<M::State>;
#[allow(unused)]
fn call_entry_action(&mut self, shared_storage: &mut M, context: &mut M::Context<'_>) {}
#[allow(unused)]
fn call_exit_action(&mut self, shared_storage: &mut M, context: &mut M::Context<'_>) {}
fn superstate(&mut self) -> Option<M::Superstate<'_>>
where
Self: Sized,
{
None
}
}
pub trait SuperstateExt<'a, M>: Superstate<M>
where
M: IntoStateMachine,
Self: Sized,
M::State: 'a,
for<'b> M::Superstate<'b>: Superstate<M>,
{
fn same_state(lhs: &M::Superstate<'_>, rhs: &M::Superstate<'_>) -> bool {
use core::mem::{discriminant, transmute, Discriminant};
let lhs: Discriminant<M::Superstate<'_>> = unsafe { transmute(discriminant(lhs)) };
let rhs: Discriminant<M::Superstate<'_>> = unsafe { transmute(discriminant(rhs)) };
lhs == rhs
}
fn depth(&mut self) -> usize {
match self.superstate() {
Some(mut superstate) => superstate.depth() + 1,
None => 1,
}
}
fn common_ancestor_depth(
mut source: M::Superstate<'_>,
mut target: M::Superstate<'_>,
) -> usize {
match source.depth().cmp(&target.depth()) {
Ordering::Equal => match Self::same_state(&source, &target) {
true => source.depth(),
false => match (source.superstate(), target.superstate()) {
(Some(source), Some(target)) => Self::common_ancestor_depth(source, target),
_ => 0,
},
},
Ordering::Greater => match source.superstate() {
Some(superstate) => Self::common_ancestor_depth(superstate, target),
None => 0,
},
Ordering::Less => match target.superstate() {
Some(superstate) => Self::common_ancestor_depth(source, superstate),
None => 0,
},
}
}
fn handle(
&mut self,
shared_storage: &mut M,
event: &M::Event<'_>,
context: &mut M::Context<'_>,
) -> Outcome<M::State>
where
Self: Sized,
{
let outcome = self.call_handler(shared_storage, event, context);
match outcome {
Outcome::Handled => Outcome::Handled,
Outcome::Super => match self.superstate() {
Some(mut superstate) => {
M::before_dispatch(
shared_storage,
StateOrSuperstate::Superstate(&superstate),
event,
context
);
let outcome = superstate.handle(shared_storage, event, context);
M::after_dispatch(
shared_storage,
StateOrSuperstate::Superstate(&superstate),
event,
context
);
outcome
}
None => Outcome::Super,
},
Outcome::Transition(state) => Outcome::Transition(state),
}
}
fn enter(&mut self, shared_storage: &mut M, context: &mut M::Context<'_>, mut levels: usize) {
match levels {
0 => (),
1 => self.call_entry_action(shared_storage, context),
_ => {
if let Some(mut superstate) = self.superstate() {
levels -= 1;
superstate.enter(shared_storage, context, levels);
}
self.call_entry_action(shared_storage, context);
}
}
}
fn exit(&mut self, shared_storage: &mut M, context: &mut M::Context<'_>, mut levels: usize) {
match levels {
0 => (),
1 => self.call_exit_action(shared_storage, context),
_ => {
self.call_exit_action(shared_storage, context);
if let Some(mut superstate) = self.superstate() {
levels -= 1;
superstate.exit(shared_storage, context, levels);
}
}
}
}
}
impl<M> Superstate<M> for ()
where
M: IntoStateMachine,
{
fn call_handler(
&mut self,
_: &mut M,
_: &M::Event<'_>,
_: &mut M::Context<'_>,
) -> Outcome<M::State> {
Outcome::Handled
}
fn call_entry_action(&mut self, _: &mut M, _: &mut M::Context<'_>) {}
fn call_exit_action(&mut self, _: &mut M, _: &mut M::Context<'_>) {}
fn superstate(&mut self) -> Option<M::Superstate<'_>>
where
Self: Sized,
{
None
}
}
impl<'a, T, M> SuperstateExt<'a, M> for T
where
T: Superstate<M>,
M: IntoStateMachine,
M::State: 'a,
for<'b> M::Superstate<'b>: Superstate<M>,
{
}