vex_rt/
machine.rs

1//! Support for synchronous and asynchronous state machines.
2
3use alloc::{boxed::Box, sync::Arc};
4use core::{
5    any::Any,
6    marker::{Send, Sync},
7};
8
9use crate::rtos::{Context, ContextWrapper, Mutex, Promise};
10
11/// Denotes a type which represents a state machine.
12pub trait StateMachine {
13    /// The state type used by the state machine.
14    type State;
15
16    /// Gets the current state of the state machine.
17    fn state(&self) -> Self::State;
18
19    /// Transitions the state machine to a new state.
20    ///
21    /// Returns the context in which the new state is running.
22    fn transition(&self, state: Self::State) -> Context;
23}
24
25/// Data structure used by state machines generated using the
26/// [`state_machine!`](crate::state_machine!) macro.
27pub struct StateMachineData<S: Clone> {
28    state: S,
29    listener: ListenerBox,
30    ctxw: ContextWrapper,
31}
32
33impl<S: Clone> StateMachineData<S> {
34    /// Constructs a new data structure, wrapped in a [`StateMachineHandle`].
35    pub fn new_wrapped(state: S) -> StateMachineHandle<S> {
36        Arc::new(Mutex::new(Self {
37            state,
38            listener: ListenerBox(None),
39            ctxw: ContextWrapper::new(),
40        }))
41    }
42
43    /// Gets a reference to the current state.
44    pub fn state(&self) -> &S {
45        &self.state
46    }
47
48    /// Begins executing a new state.
49    ///
50    /// Returns the state to execute and the context for the execution.
51    pub fn begin(&mut self) -> (S, Context) {
52        (
53            self.state.clone(),
54            if let Some(ctx) = self.ctxw.current() {
55                ctx.clone()
56            } else {
57                self.ctxw.replace()
58            },
59        )
60    }
61
62    /// Instructs a transition to a new state.
63    ///
64    /// Returns the context under which that state will execute.
65    pub fn transition(&mut self, state: S) -> Context {
66        self.state = state;
67        self.listener.clear();
68        self.ctxw.replace()
69    }
70
71    /// Instructs a transition to a new state, given a parent context to limit
72    /// the execution of the state body.
73    pub fn transition_ext(&mut self, ctx: Context, state: S) -> Context {
74        self.state = state;
75        self.listener.clear();
76        self.ctxw.replace_ext(ctx)
77    }
78
79    /// Produces a promise which listens for the result of the current state.
80    ///
81    /// The promise will only be resolved if `T` matches the result type of the
82    /// current state.
83    pub fn listen<T: Send + Sync>(&mut self) -> Promise<T> {
84        self.listener.listen::<T>()
85    }
86
87    /// Resolves the listener promise, if there is one and its type matches.
88    pub fn resolve<T: 'static>(&mut self, result: T) {
89        self.listener.resolve::<T>(result);
90    }
91}
92
93/// A shared instance of [`StateMachineData`].
94pub type StateMachineHandle<S> = Arc<Mutex<StateMachineData<S>>>;
95
96struct ListenerBox(Option<Box<dyn Any + Send>>);
97
98impl ListenerBox {
99    fn clear(&mut self) {
100        self.0.take();
101    }
102
103    fn listen<T: Send + Sync>(&mut self) -> Promise<T> {
104        if self.0.is_some() {
105            panic!("cannot override listener")
106        }
107
108        let (promise, resolve) = Promise::new();
109        let mut resolve = Some(resolve);
110        let f = move |result| {
111            if let Some(resolve) = resolve.take() {
112                resolve(result);
113            }
114        };
115
116        let inner_box: Box<dyn FnMut(T) + Send> = Box::new(f);
117        let outer_box: Box<dyn Any + Send> = Box::new(inner_box);
118        self.0 = Some(outer_box);
119
120        promise
121    }
122
123    fn resolve<T: 'static>(&mut self, result: T) {
124        if let Some(mut boxed) = self.0.take() {
125            if let Some(resolve) = boxed.downcast_mut::<Box<dyn FnMut(T) + Send>>() {
126                resolve(result)
127            }
128        }
129    }
130}
131
132/// The possible results for state processing in a state machine.
133///
134/// `T` is the output type of the state, and `S` is the state type of the state
135/// machine.
136#[non_exhaustive]
137#[derive(Clone, Copy, Debug, PartialEq, Eq)]
138pub enum StateResult<T, S> {
139    /// Finishes processing the state with the given output, without
140    /// transitioning to a new state.
141    Simple(T),
142    /// Finishes processing the state with the given output and transitions to
143    /// the given next state.
144    Transition(T, S),
145}
146
147impl<T, S> StateResult<T, S> {
148    /// Produces the result as a tuple of output value and optional next state.
149    pub fn into_tuple(self) -> (T, Option<S>) {
150        match self {
151            StateResult::Simple(result) => (result, None),
152            StateResult::Transition(result, next) => (result, Some(next)),
153        }
154    }
155}