use alloc::{boxed::Box, string::ToString, sync::Arc};
use core::{
any::Any,
marker::{Send, Sync},
};
use crate::rtos::{Context, ContextWrapper, GenericSleep, Mutex, Promise, Selectable, Task};
pub trait StateMachine {
type State: StateType;
fn state(&self) -> Self::State;
fn transition(&self, state: Self::State) -> Context;
}
pub trait StateType: Clone + Send + Sync + 'static {
const STATE_MACHINE_NAME: &'static str;
fn name(&self) -> &str;
}
pub struct StateMachineData<S: StateType> {
state: S,
task: Task,
next_frame: Option<StateFrame<S>>,
ctxw: ContextWrapper,
}
impl<S: StateType> StateMachineData<S> {
pub fn new_wrapped(state: S) -> StateMachineHandle<S> {
let mut ctxw = ContextWrapper::new_ext(Some(S::STATE_MACHINE_NAME.to_string()));
Arc::new(Mutex::new(Self {
state: state.clone(),
task: Task::current(),
next_frame: Some(StateFrame {
state,
ctx: ctxw.replace(),
listener: ListenerBox(None),
}),
ctxw,
}))
}
pub fn state(&self) -> &S {
&self.state
}
pub fn set_task(&mut self, task: Task) {
self.task = task;
}
pub fn try_begin(&mut self) -> Option<StateFrame<S>> {
let frame = self.next_frame.take()?;
#[cfg(feature = "logging")]
log::debug!(
target: S::STATE_MACHINE_NAME,
"{} -> {} by request {}",
self.state.name(),
frame.state.name(),
frame.ctx.name(),
);
self.state = frame.state.clone();
Some(frame)
}
pub fn tail_transition(&mut self, frame: StateFrame<S>, state: S) -> StateFrame<S> {
#[cfg(feature = "logging")]
log::debug!(
target: S::STATE_MACHINE_NAME,
"{} -> {} by tail-transition",
self.state.name(),
state.name(),
);
self.state = state.clone();
StateFrame { state, ..frame }
}
pub fn transition(&mut self, state: S) -> TransitionBuilder<'_, S> {
self.transition_impl(None, state)
}
pub fn transition_ext<'a>(
&'a mut self,
ctx: &'a Context,
state: S,
) -> TransitionBuilder<'a, S> {
self.transition_impl(Some(ctx), state)
}
fn transition_impl<'a>(
&'a mut self,
ctx: Option<&'a Context>,
state: S,
) -> TransitionBuilder<'a, S> {
#[cfg(feature = "logging")]
log::trace!(target: S::STATE_MACHINE_NAME, "requesting {}", state.name());
TransitionBuilder {
state,
ctx,
listener: ListenerBox(None),
data: self,
}
}
}
pub type StateMachineHandle<S> = Arc<Mutex<StateMachineData<S>>>;
pub fn state_begin<S: StateType>(
handle: &StateMachineHandle<S>,
) -> impl Selectable<Output = StateFrame<S>> + '_ {
struct BeginSelect<'a, S: StateType>(&'a StateMachineHandle<S>);
impl<'a, S: StateType> Selectable for BeginSelect<'a, S> {
type Output = StateFrame<S>;
fn poll(self) -> Result<Self::Output, Self> {
self.0.lock().try_begin().ok_or(self)
}
fn sleep(&self) -> crate::prelude::GenericSleep {
if self.0.lock().next_frame.is_some() {
GenericSleep::Ready
} else {
GenericSleep::NotifyTake(None)
}
}
}
BeginSelect(handle)
}
pub struct TransitionBuilder<'a, S: StateType> {
state: S,
ctx: Option<&'a Context>,
listener: ListenerBox,
data: &'a mut StateMachineData<S>,
}
impl<'a, S: StateType> TransitionBuilder<'a, S> {
pub fn finish(self) -> Context {
let ctx = self.data.ctxw.replace_ext(&self.ctx);
self.data.next_frame = Some(StateFrame {
state: self.state,
ctx: ctx.clone(),
listener: self.listener,
});
self.data.task.notify();
ctx
}
pub fn listen<T: Send + Sync>(&mut self) -> Promise<T> {
self.listener.listen()
}
}
pub struct StateFrame<S: StateType> {
pub state: S,
pub ctx: Context,
listener: ListenerBox,
}
impl<S: StateType> StateFrame<S> {
pub fn resolve<T: 'static>(&mut self, result: T) {
self.listener.resolve(result);
}
}
struct ListenerBox(Option<Box<dyn Any + Send + Sync>>);
impl ListenerBox {
fn listen<T: Send + Sync>(&mut self) -> Promise<T> {
if self.0.is_some() {
panic!("cannot override listener")
}
let (promise, resolve) = Promise::new();
let mut resolve = Some(resolve);
let f = move |result| {
if let Some(resolve) = resolve.take() {
resolve(result);
}
};
let inner_box: Box<dyn FnMut(T) + Send + Sync> = Box::new(f);
let outer_box: Box<dyn Any + Send + Sync> = Box::new(inner_box);
self.0 = Some(outer_box);
promise
}
fn resolve<T: 'static>(&mut self, result: T) {
if let Some(mut boxed) = self.0.take() {
if let Some(resolve) = boxed.downcast_mut::<Box<dyn FnMut(T) + Send>>() {
resolve(result)
}
}
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StateResult<T, S> {
Simple(T),
Transition(T, S),
}
impl<T, S> StateResult<T, S> {
pub fn into_tuple(self) -> (T, Option<S>) {
match self {
StateResult::Simple(result) => (result, None),
StateResult::Transition(result, next) => (result, Some(next)),
}
}
}