use crate::blocking::{IntoStateMachine, Superstate, SuperstateExt};
use crate::{Outcome, StateOrSuperstate};
pub trait State<M>
where
Self: Sized,
M: IntoStateMachine,
{
fn call_handler(
&mut self,
shared_storage: &mut M,
event: &M::Event<'_>,
context: &mut M::Context<'_>,
) -> Outcome<Self>;
#[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<'_>> {
None
}
}
pub trait StateExt<'a, M>: State<M>
where
M: IntoStateMachine<State = Self>,
M::State: 'a,
for<'b> M::Superstate<'b>: Superstate<M>,
{
fn same_state(lhs: &Self, rhs: &Self) -> bool {
core::mem::discriminant(lhs) == core::mem::discriminant(rhs)
}
fn depth(&mut self) -> usize {
match self.superstate() {
Some(mut superstate) => superstate.depth() + 1,
None => 1,
}
}
fn common_ancestor_depth(source: &mut Self, target: &mut Self) -> usize {
if Self::same_state(source, target) {
return source.depth();
}
match (source.superstate(), target.superstate()) {
(Some(source), Some(target)) => M::Superstate::common_ancestor_depth(source, target),
_ => 0,
}
}
fn transition_path(&mut self, target: &mut Self) -> (usize, usize) {
if Self::same_state(self, target) {
return (1, 1);
}
let source_depth = self.depth();
let target_depth = target.depth();
if let (Some(source), Some(target)) = (self.superstate(), target.superstate()) {
let common_state_depth = M::Superstate::common_ancestor_depth(source, target);
(
source_depth - common_state_depth,
target_depth - common_state_depth,
)
} else {
(source_depth, target_depth)
}
}
fn handle(
&mut self,
shared_storage: &mut M,
event: &M::Event<'_>,
context: &mut M::Context<'_>,
) -> Outcome<Self>
where
Self: Sized,
{
M::before_dispatch(
shared_storage,
StateOrSuperstate::State(self),
event,
context,
);
let outcome = self.call_handler(shared_storage, event, context);
M::after_dispatch(
shared_storage,
StateOrSuperstate::State(self),
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<'_>, levels: usize) {
match levels {
0 => (),
1 => self.call_entry_action(shared_storage, context),
_ => {
if let Some(mut superstate) = self.superstate() {
superstate.enter(shared_storage, context, levels - 1);
}
self.call_entry_action(shared_storage, context);
}
}
}
fn exit(&mut self, shared_storage: &mut M, context: &mut M::Context<'_>, 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() {
superstate.exit(shared_storage, context, levels - 1);
}
}
}
}
}
impl<'a, T, M> StateExt<'a, M> for T
where
T: State<M>,
M: IntoStateMachine<State = T>,
M::State: 'a,
for<'b> M::Superstate<'b>: Superstate<M>,
{
}