odem_rs_core/
fsm.rs

1//! A module providing infrastructure for building safe and controlled type
2//! state machines in ODEM-rs.
3//!
4//! This module defines a set of traits, types, and macros that facilitate the
5//! creation and management of state machines with enforced state transitions.
6//! It ensures that state changes are tracked and validated at compile time.
7//!
8//! # Overview
9//!
10//! The key components provided by this module are:
11//!
12//! - **Macros**:
13//!     - [`fsm`]: A macro to simplify the generation of state machines,
14//!       allowing for concise definitions of states and transitions.
15//!
16//! - **Types**:
17//!     - [`Ephemeral`]: A token type used to enforce one-time usage, ensuring
18//!       that the number of token witnesses is always known.
19//!     - [`StateMachine`]: A wrapper type for custom state machines generated
20//!       through the `fsm!` macro. It maintains the state and controls
21//!       transitions.
22//!     - [`TransitionError`]: An error type indicating issues that may occur
23//!       during state transitions, such as invalidation or borrowing conflicts.
24//!
25//! - **Traits**:
26//!     - [`Stateful`]: Marks a type that supports methods using `Ephemeral`
27//!       tokens to track state changes. It provides hooks for tracking the
28//!       number of tokens *witnessing* the current state.
29//!     - [`Rebrand`]: Defines a generalized associated type (GAT) that
30//!       corresponds with a rebranded version of that type.
31//!     - [`Brand`]: Extends `Stateful` types with a method to create an
32//!       `Ephemeral` token, allowing for state tracking through branding.
33//!     - [`Debrand`]: Allows branded instances to temporarily escape state
34//!       tracking to execute closures that may rebrand the instance.
35//!     - [`Transition`]: Defines safe state transitions between states
36//!       `U` and `V`, given a token witness to the current state.
37//!
38//! # Usage
39//!
40//! This module is intended for defining state machines where state transitions
41//! need to be controlled and validated. By using the provided traits and types,
42//! you can enforce that certain methods are only callable in specific states
43//! and that transitions between states are valid. This leads to safer and
44//! hopefully more maintainable code.
45//!
46//! The `fsm` macro can be used to generate a custom state machine with defined
47//! states and transitions, reducing boilerplate and potential for errors.
48//!
49//! # Example
50//!
51//! ```
52//! # use odem_rs_core::fsm::*;
53//! # use core::clone::Clone;
54//! // Define a state machine with three states: `Idle`, `Exec`, and `Done`.
55//! fsm! {
56//!     pub enum States<C: Clone> {
57//!         Idle(C) -> { Exec },
58//!         Exec -> { Idle, Done },
59//!         Done -> {}
60//!     }
61//! }
62//!
63//! fn main() {
64//!     use States::*;
65//!     let context = "MyContext".to_string();
66//!     let machine = StateMachine::new(Idle(context.clone()));
67//!
68//!     // Transition from Initialized to Running
69//!     machine.brand(|sm, once| {
70//!         // Assert the idle state
71//!         let idle = sm.token(once).into_idle().unwrap();
72//!         // Transition into the running state
73//!         let exec: token::Exec<'_> = sm.transition(idle, ());
74//!         // Do something with the running state
75//!         let done: token::Done<'_> = sm.transition(exec, ());
76//!     });
77//! }
78//! ```
79//!
80//! # Safety
81//!
82//! The use of [`Ephemeral`] tokens and the `brand` and `debrand` methods enforce at
83//! compile time that state transitions are valid and that state changes do not
84//! occur while other parts of the program hold a token witness. This helps
85//! prevent bugs related to invalid state transitions and ensures that the state
86//! machine remains in a valid state throughout its lifecycle.
87//!
88//! Specifically, it is made impossible to use token reflecting the state of one
89//! state machine to affect transitions in another one.
90//!
91//! For example, this machine attempts to use a token made for another state
92//! machine, leading to a compiler error:
93//!
94//! ```compile_fail,E0521
95//! # use odem_rs_core::fsm::*;
96//! // Define a state machine with three states: `Idle`, `Exec`, and `Done`.
97//! fsm! {
98//!     pub enum States {
99//!         Idle -> { Exec },
100//!         Exec -> { Idle, Done },
101//!         Done -> {}
102//!     }
103//! }
104//!
105//! # fn main() {
106//! let m1 = StateMachine::new(States::Idle);
107//! let m2 = StateMachine::new(States::Exec);
108//!
109//! m1.brand(|_, once| {
110//!     // Use the wrong brand of Ephemeral token; should (and does) not compile!
111//!     let exec = m2.token(once).into_exec().unwrap();
112//!     let _: token::Done<'_> = m1.transition(exec, ());
113//! });
114//! # }
115//! ```
116//!
117//! # Notes
118//!
119//! While the module provides mechanisms to prevent multiple tokens from
120//! invalidating each other, it is possible to create multiple tokens via
121//! repeated calls to `brand`, which would lead to the state being frozen in a
122//! read-only mode. Care should be taken to manage tokens appropriately and
123//! ensure that branding scopes are correctly entered and exited.
124
125use core::{
126	cell::{BorrowMutError, Cell, Ref, RefCell},
127	marker::PhantomData,
128	ops::Deref,
129	pin::Pin,
130};
131
132/* *********************************************************** Exposed Traits */
133
134/// Trait implemented by the [`fsm`]-macro.
135pub trait FSM {
136	/// Type of generically branded token.
137	type Token<'b>;
138
139	/// Type of the stateless enumeration of all possible states.
140	type Erased;
141
142	/// Private trait function to convert a branded [`Ephemeral`] into an equally
143	/// branded machine-specific token type.
144	///
145	/// The function argument of type [`Private`] is meant to prevent users from
146	/// calling this method directly rather than calling the methods on the
147	/// complex state machines, like [`StateMachine`].
148	///
149	/// Calling the method directly has the potential to invalidate the branding
150	/// since `'brand` is chosen by the caller and therefore could be applied
151	/// to `Ephemeral` token from other instances.
152	fn token<'b>(this: &Self, once: Ephemeral<'b>, _: Private) -> Self::Token<'b>;
153
154	/// Erases the configuration-specific payloads from the enumeration,
155	/// returning a type-erased version.
156	fn erased(&self) -> Self::Erased;
157
158	/// Returns the name of the current state as a string.
159	fn label(&self) -> &'static str;
160}
161
162/// Marks a type for supporting extra methods using [`Ephemeral`]-tokens to keep
163/// track of state changes.
164pub trait Stateful {
165	/// Associated marker type for the branding.
166	type Brand; // = `&'brand ()`
167
168	/// Method hook used to register the creation of another [`Ephemeral`]-token
169	/// for this instance.
170	///
171	/// # Safety
172	/// Calling this method increases the count of `Ephemeral`-token emitted
173	/// by the object. Only call this method manually if one of these token
174	/// flows into an object that will release it later in order to freeze the
175	/// state.
176	unsafe fn enter(&self);
177
178	/// Method hook used to register the destruction of a [`Ephemeral`]-token
179	/// for this instance.
180	///
181	/// # Safety
182	/// Calling this method reduces the count of `Ephemeral`-token which may
183	/// enable transitions to occur. Only call this method manually when
184	/// releasing a token from an object in order to unfreeze the state.
185	unsafe fn leave(&self);
186}
187
188/// Extension trait to keep track of a [`Stateful`] object's current branding.
189pub trait Rebrand<'brand>: Stateful<Brand = &'brand ()> {
190	/// Meta function that computes the rebranded version of `Self` and forces
191	/// the type to correspond with the `Self` type for one of the brandings.
192	///
193	/// This prevents the trait from being implemented like this:
194	/// ```compile_fail,E0271
195	/// # use odem_rs_core::fsm::*;
196	/// # use core::marker::PhantomData;
197	/// struct S<'b>(PhantomData<fn(&'b ()) -> &'b ()>);
198	///
199	/// impl<'b> Stateful for S<'b> {
200	///     type Brand = &'b ();
201	///     unsafe fn enter(&self) {}
202	///     unsafe fn leave(&self) {}
203	/// }
204	///
205	/// impl<'b> Rebrand<'b> for S<'b> {
206	///     type Kind<'a> = S<'a>; // legal, since Kind<'b> == S<'b>
207	/// }
208	///
209	/// struct T<'b>(PhantomData<fn(&'b ()) -> &'b ()>);
210	///
211	/// impl<'b> Stateful for T<'b> {
212	///     type Brand = &'b ();
213	///     unsafe fn enter(&self) {}
214	///     unsafe fn leave(&self) {}
215	/// }
216	///
217	/// impl<'b> Rebrand<'b> for T<'b> {
218	///     type Kind<'a> = S<'a>; // illegal, since ∄'a: Kind<'a> == T<'b>
219	/// }
220	/// ```
221	///
222	type Kind<'a>: Rebrand<'a, Kind<'brand> = Self>;
223
224	/// "Converts" a reference to `Self` into a reference of `Self::Kind`, which
225	/// is constrained to be a reference to `Self` itself, making this a no-op.
226	///
227	/// The method is required to support branding.
228	fn identity_ref(&self) -> &Self::Kind<'brand> {
229		// SAFETY: since Self::Kind<'brand> == Self, this is a no-op
230		unsafe { &*(self as *const Self as *const Self::Kind<'brand>) }
231	}
232
233	/// "Converts" a mutable reference to `Self` into a mutable reference of
234	/// `Self::Kind`, which is constrained to be a mutable reference to `Self`
235	/// itself, making this a no-op.
236	///
237	/// The method is required to support branding.
238	fn identity_mut(&mut self) -> &mut Self::Kind<'brand> {
239		// SAFETY: since Self::Kind<'brand> == Self, this is a no-op
240		unsafe { &mut *(self as *mut Self as *mut Self::Kind<'brand>) }
241	}
242}
243
244/// Extends [`Stateful`] types by a method to create a [`Ephemeral`]-token that
245/// helps to track state changes.
246pub trait Brand<F, R> {
247	/// Provides an [`Ephemeral`]-token to a closure for [`Stateful`] objects,
248	/// unlocking the brand-specific methods.
249	fn brand(self, f: F) -> R;
250}
251
252/// Extends [`Stateful`] types by a method to temporarily escape the tracking of
253/// state changes. This is necessary when state transitions may occur as a
254/// side effect of a function call, since those are blocked as long as token
255/// witnesses to other states exist.
256pub trait Debrand<'b, F, R> {
257	/// This method allows branded instances to temporarily escape their
258	/// branding in order to execute a caller-supplied closure that may rebrand
259	/// the instance.
260	///
261	/// Not using this method and branding the same instance twice freezes
262	/// the state in read-only mode, preventing transitions in order to
263	/// ensure that the state both token are witnessing doesn't change.
264	fn debrand(self, once: impl Into<Ephemeral<'b>>, f: F) -> (Ephemeral<'b>, R);
265}
266
267/// Trait allowing safe state-transitions given a token witness that is
268/// testifying to the current state and returning another token after the
269/// transition occurred.
270///
271/// Only potentially correct transitions are implemented, allowing a static
272/// compile-time check that all transitions follow a predefined flow-chart.
273pub trait Transition<'b, U, V> {
274	/// Associated type specifying the payload of the destination state `V`.
275	type Data;
276
277	/// Performs the transition between `U` and `V` given an `U`-specific token
278	/// and returning a `V`-specific token.
279	///
280	/// The method is not directly callable for the user in order to ensure that
281	/// the invariants regarding the number of token witnesses are kept. The
282	/// mechanism is enforced by the last [`Private`] argument to the function
283	/// that cannot be constructed outside of this module.
284	fn transition(this: &mut Self, curr: U, data: Self::Data, _: Private) -> V;
285}
286
287/* *************************************************************** Token Type */
288
289/// Type for a one-time-only usable token.
290///
291/// Token of this type are used as a seed for the [`brand`] method and are meant
292/// to ensure that other kinds of Token may only be created once. This is the
293/// reason why the type constructor is deliberately kept private. The *only* way
294/// to create this token is to call the `brand` method.
295///
296/// This is important because otherwise it would be possible to create two Token
297/// from the same instance and use one of them to invalidate the other one. This
298/// type of bug is prevented by the `brand`-method tracking the number of
299/// `Ephemeral` tokens released for each object in combination of transitions
300/// blocking changes to the state if more than one Token exists.
301///
302/// [`brand`]: Brand::brand
303#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
304pub struct Ephemeral<'brand>(PhantomData<fn(&'brand ()) -> &'brand ()>);
305
306/* ****************************************** Marker Type for Private Details */
307
308/// Marker type to enable generating private functions in a public trait
309/// interface.
310pub struct Private(());
311
312/* ************************************************************ Complex State */
313
314/// Wrapper type for custom state machines generated through the
315/// [`fsm`] macro.
316pub struct StateMachine<'b, T: ?Sized> {
317	/// Invariant lifetime for branding.
318	_mark: PhantomData<fn(&'b ()) -> &'b ()>,
319
320	/// The number of open scopes/[`Ephemeral`] token emitted.
321	///
322	/// Freezes state transitions if larger than 1.
323	token: Cell<u32>,
324
325	/// Mutable access to the inner state generated by the macro.
326	state: RefCell<T>,
327}
328
329impl<T> StateMachine<'static, T> {
330	/// Creates a new state machine using the state `T`.
331	pub const fn new(inner: T) -> Self {
332		Self {
333			_mark: PhantomData,
334			token: Cell::new(0),
335			state: RefCell::new(inner),
336		}
337	}
338}
339
340impl<'b, T: FSM> StateMachine<'b, T> {
341	/// Returns an enumeration containing token witnesses for the current state.
342	pub fn token(&self, once: Ephemeral<'b>) -> T::Token<'b> {
343		T::token(&*self.borrow(), once, Private(()))
344	}
345
346	/// Returns a type-erased version of the current state, i.e. an enumeration
347	/// of just the states, without additional payload.
348	pub fn erased(&self) -> T::Erased {
349		T::erased(&*self.borrow())
350	}
351
352	/// Returns the name of the current state as a string.
353	pub fn label(&self) -> &'static str {
354		T::label(&*self.borrow())
355	}
356
357	/// Performs a state transition given the current state and the payload of
358	/// the follow-up state, returning a token witness for the next state.
359	///
360	/// This method will panic if the transition would invalidate other token
361	/// witnesses. See [`Self::try_transition`] for a non-panicking variant.
362	pub fn transition<U, V>(&self, curr: U, data: T::Data) -> V
363	where
364		T: Transition<'b, U, V>,
365	{
366		match self.try_transition(curr, data) {
367			Ok(v) => v,
368			Err(err) => panic!(
369				"cannot transition from '{}' to '{}': {err}",
370				self.label(),
371				core::any::type_name::<V>(),
372			),
373		}
374	}
375
376	/// Attempts to perform a state transition given the current state and the
377	/// payload of the follow-up state, returning a token witness for the next
378	/// state if successful and the previous token witness on error.
379	///
380	/// It is not possible to transition if other token witnesses for the same
381	/// state machine would be invalidated by the transition. If you simply
382	/// want to update the data associated with a state without changing it,
383	/// consider using [`Self::update`] which doesn't have this restriction.
384	pub fn try_transition<U, V>(&self, curr: U, data: T::Data) -> Result<V, TransitionError<U>>
385	where
386		T: Transition<'b, U, V>,
387	{
388		match self.token.get() {
389			0 => unreachable!("transitioning without tokens should be impossible"),
390			1 => match self.state.try_borrow_mut() {
391				Ok(mut state) => Ok(T::transition(&mut *state, curr, data, Private(()))),
392				Err(err) => Err(TransitionError::Borrow(curr, err)),
393			},
394			n => Err(TransitionError::Invalidation(curr, n)),
395		}
396	}
397
398	/// Updates the internal data of a state without changing it.
399	///
400	/// This is allowed, even if other token witnesses exist as it cannot
401	/// invalidate them and will only panic if the state cannot be modified due
402	/// to being borrowed already.
403	pub fn update<U>(&self, curr: U, data: T::Data) -> U
404	where
405		T: Transition<'b, U, U>,
406	{
407		T::transition(&mut *self.state.borrow_mut(), curr, data, Private(()))
408	}
409
410	/// Borrows the inner state.
411	#[track_caller]
412	pub fn borrow(&self) -> Ref<'_, T> {
413		self.state.borrow()
414	}
415}
416
417impl<T: Default> Default for StateMachine<'static, T> {
418	fn default() -> Self {
419		Self::new(T::default())
420	}
421}
422
423impl<'b, T> Stateful for StateMachine<'b, T> {
424	type Brand = &'b ();
425
426	#[inline]
427	unsafe fn enter(&self) {
428		self.token.set(self.token.get() + 1);
429	}
430
431	#[inline]
432	unsafe fn leave(&self) {
433		self.token.set(self.token.get() - 1);
434	}
435}
436
437impl<'b, T> Rebrand<'b> for StateMachine<'b, T> {
438	type Kind<'a> = StateMachine<'a, T>;
439}
440
441/// Enumeration of errors that may happen during state transitions.
442#[derive(thiserror::Error)]
443pub enum TransitionError<T> {
444	/// Signals that a transition failed because other token witnesses would
445	/// have been invalidated.
446	#[error("cannot transition while {1} references exist")]
447	Invalidation(T, u32),
448	/// Signals that a transition failed because the state was already borrowed
449	/// and thus could not be overwritten.
450	#[error("cannot transition while state is borrowed")]
451	Borrow(T, BorrowMutError),
452}
453
454/* ************************************************************************** */
455
456/// Scope-guard that ensures the correct counting of branding scopes even
457/// in case of a panic.
458struct Guard<T: Stateful, const E: bool = true>(T);
459
460impl<T: Stateful> Guard<T, true> {
461	/// Creates a new scope guard, entering the branding scope.
462	fn enter(value: T) -> Self {
463		unsafe {
464			value.enter();
465		}
466		Self(value)
467	}
468}
469
470impl<T: Stateful> Guard<T, false> {
471	/// Creates a new scope guard, leaving the branding scope.
472	fn leave(value: T) -> Self {
473		unsafe {
474			value.leave();
475		}
476		Self(value)
477	}
478}
479
480impl<T: Stateful, const E: bool> Drop for Guard<T, E> {
481	/// Drops the scope guard, exiting or entering (depending on `E`) the scope.
482	fn drop(&mut self) {
483		if E {
484			unsafe {
485				self.0.leave();
486			}
487		} else {
488			unsafe {
489				self.0.enter();
490			}
491		}
492	}
493}
494
495/* **************************************************** Trait Implementations */
496
497// implement the Stateful trait for all references to Stateful objects
498impl<T: Deref<Target: Stateful>> Stateful for T {
499	type Brand = <T::Target as Stateful>::Brand;
500
501	unsafe fn enter(&self) {
502		unsafe {
503			(**self).enter();
504		}
505	}
506
507	unsafe fn leave(&self) {
508		unsafe {
509			(**self).leave();
510		}
511	}
512}
513
514// Implement the Brand method for different kinds of references to Brandable types
515impl<'b, T, F, R> Brand<F, R> for &T
516where
517	T: Rebrand<'b>,
518	F: for<'a> FnOnce(&T::Kind<'a>, Ephemeral<'a>) -> R,
519{
520	fn brand(self, f: F) -> R {
521		f(Guard::enter(self.identity_ref()).0, Ephemeral(PhantomData))
522	}
523}
524
525impl<'b, T, F, R> Brand<F, R> for &mut T
526where
527	T: Rebrand<'b>,
528	F: for<'a> FnOnce(&mut T::Kind<'a>, Ephemeral<'a>) -> R,
529{
530	fn brand(self, f: F) -> R {
531		f(Guard::enter(self.identity_mut()).0, Ephemeral(PhantomData))
532	}
533}
534
535impl<'b, T, F, R> Brand<F, R> for Pin<&T>
536where
537	T: Rebrand<'b>,
538	F: for<'a> FnOnce(Pin<&T::Kind<'a>>, Ephemeral<'a>) -> R,
539{
540	fn brand(self, f: F) -> R {
541		// SAFETY: T: Brandable<'brand> implies T::Kind<'brand> = Self
542		// therefore the cast below is safe
543		let this = unsafe { Pin::map_unchecked(self, T::identity_ref) };
544
545		f(Guard::enter(this).0.as_ref(), Ephemeral(PhantomData))
546	}
547}
548
549impl<'b, T, F, R> Brand<F, R> for Pin<&mut T>
550where
551	T: Rebrand<'b>,
552	F: for<'a> FnOnce(Pin<&mut T::Kind<'a>>, Ephemeral<'a>) -> R,
553{
554	fn brand(self, f: F) -> R {
555		// SAFETY: T: Brandable<'brand> implies T::Kind<'brand> = Self
556		// therefore the cast below is safe
557		let this = unsafe { Pin::map_unchecked_mut(self, T::identity_mut) };
558
559		f(Guard::enter(this).0.as_mut(), Ephemeral(PhantomData))
560	}
561}
562
563impl<'b, T, F, R> Brand<F, R> for crate::ptr::Irc<T>
564where
565	T: crate::ptr::IntrusivelyCounted + Rebrand<'b, Kind<'b>: crate::ptr::IntrusivelyCounted>,
566	F: for<'a> FnOnce(&crate::ptr::Irc<T::Kind<'a>>, Ephemeral<'a>) -> R,
567{
568	fn brand(self, f: F) -> R {
569		// SAFETY: T: Brandable<'brand> implies T::Kind<'brand> = Self
570		// therefore the cast below is safe
571		let this = crate::ptr::Irc::map(self, T::identity_ref);
572
573		f(&Guard::enter(this).0, Ephemeral(PhantomData))
574	}
575}
576
577// Implement the Debrand method for different kinds of references to Brandable types
578impl<'b, T, F, R> Debrand<'b, F, R> for &T
579where
580	T: Rebrand<'b>,
581	F: FnOnce(&T) -> R,
582{
583	fn debrand(self, once: impl Into<Ephemeral<'b>>, f: F) -> (Ephemeral<'b>, R) {
584		// can't risk having drop not called, so it's not passed down
585		let guard = Guard::leave(self);
586		(once.into(), f(guard.0))
587	}
588}
589
590impl<'b, T, F, R> Debrand<'b, F, R> for Pin<&T>
591where
592	T: Rebrand<'b>,
593	F: FnOnce(Pin<&T>) -> R,
594{
595	fn debrand(self, once: impl Into<Ephemeral<'b>>, f: F) -> (Ephemeral<'b>, R) {
596		// can't risk having drop not called, so it's not passed down
597		let guard = Guard::leave(self);
598		(once.into(), f(guard.0))
599	}
600}
601
602impl<'b, T, F, R> Debrand<'b, F, R> for &mut T
603where
604	T: Rebrand<'b>,
605	F: FnOnce(&mut T) -> R,
606{
607	fn debrand(self, once: impl Into<Ephemeral<'b>>, f: F) -> (Ephemeral<'b>, R) {
608		// can't risk having drop not called, so it's not passed down
609		let guard = Guard::leave(self);
610		(once.into(), f(guard.0))
611	}
612}
613
614impl<'b, T, F, R> Debrand<'b, F, R> for Pin<&mut T>
615where
616	T: Rebrand<'b>,
617	F: FnOnce(Pin<&mut T>) -> R,
618{
619	fn debrand(self, once: impl Into<Ephemeral<'b>>, f: F) -> (Ephemeral<'b>, R) {
620		// can't risk having drop not called, so it's not passed down
621		let mut guard = Guard::leave(self);
622		(once.into(), f(guard.0.as_mut()))
623	}
624}
625
626/* ********************************************************* Continuation Type States */
627
628/// A macro to automate the generation of type state machines.
629///
630/// The macro takes a definition of states and their possible transitions. It
631/// generates:
632/// - An enum representing all states.
633/// - An equivalent “erased” enum without payloads.
634/// - A set of token types for each state (and possibly for groups of states,
635///   if groups are defined).
636/// - Implementations of [`Transition`] for each defined state transition,
637///   ensuring at compile time that only valid transitions are allowed.
638///
639/// The macro also generates convenience methods to check the current state and
640/// to convert between token types. It can only be called from the module scope,
641/// not from the function scope, since it creates multiple modules.
642///
643/// # Example Usage
644///
645/// Here is a simple invocation of the macro for a finite state machine with
646/// three states:
647///
648/// ```
649/// # mod test {
650/// # use odem_rs_core::fsm::*;
651/// // Define a state machine with three states: `Idle`, `Exec`, and `Done`.
652/// fsm! {
653///     pub enum States {
654///         Idle -> { Exec },
655///         Exec -> { Idle, Done },
656///         Done -> {}
657///     }
658/// }
659/// # }
660/// ```
661///
662/// The macro also supports a single generic argument with a single trait bound.
663///
664/// ```
665/// # mod test {
666/// # use odem_rs_core::fsm::*;
667/// # use core::clone::Clone;
668/// fsm! {
669///     pub enum States<C: Clone> {
670///         Idle(C) -> { Exec },
671///         Exec -> { Idle, Done },
672///         Done -> {}
673///     }
674/// }
675/// # }
676/// ```
677///
678/// There is an implicit trait bound of `?Sized` but it can be overridden by
679/// choosing a trait bound that implies `Sized`. For technical reasons, at most
680/// one generic argument with one trait bound is supported at the moment.
681///
682/// The macro also allows defining meta states that are collections of existing
683/// states:
684///
685/// ```
686/// # mod test {
687/// # use odem_rs_core::fsm::*;
688/// fsm! {
689///     pub enum States {
690///         Idle -> { Exec },
691///         Exec -> { Idle, Done },
692///         Done -> {}
693///     }
694///     
695///     pub Any = { Idle, Exec, Done };
696///     pub Live: Any = { Idle, Exec };
697/// }
698/// # }
699/// ```
700///
701/// The `Live` meta state is marked as a subset of the `Any` meta state,
702/// allowing it to convert into that state.
703#[doc(hidden)]
704#[macro_export]
705macro_rules! __fsm {
706	(
707		$(#[$DOC:meta])*
708		$V:vis enum $E:ident $(<$C:ident: $W:ty>)? {
709			$($(#[$SDOC:meta])* $S:ident $(($T:ty))? -> {$($D:ident),* $(,)?}),* $(,)?
710		}
711
712		$(
713			$(#[$MDOC:meta])*
714			$MV:vis $MS:ident $(: $MB:ident)? = {$($MT:ident),* $(,)?};
715		)*
716	) => {paste::paste! {
717		// generate the main enumeration
718		#[cfg_attr(doc, aquamarine::aquamarine)]
719		$(#[$DOC])*
720		#[doc = "```mermaid"]
721		#[doc = "---"]
722		#[doc = "title: " $E " Transition Diagram"]
723		#[doc = "---"]
724		#[doc = "flowchart LR"]
725		#[doc = $($("\t" $S "{{" $S "}}-->" $D "{{" $D "}}\n")*)*]
726		#[doc = "```"]
727		$V enum $E $(<$C: ?Sized + $W>)* {
728			$(
729				$(#[$SDOC])*
730				$S $(($T))*
731			),*
732		}
733
734		impl<$($C: ?Sized + $W)*> $crate::fsm::FSM for $E $(<$C>)* {
735			type Token<'brand> = token::$E<'brand>;
736			type Erased = erased::$E;
737
738			fn token<'brand>(this: &Self, once: Ephemeral<'brand>, _: Private) -> Self::Token<'brand> {
739				match this {$(
740					Self::$S {..} => token::$E::$S(token::$S(once))
741				),*}
742			}
743
744			fn erased(&self) -> Self::Erased {
745				match self {$(
746					Self::$S {..} => erased::$E::$S
747				),*}
748			}
749
750			fn label(&self) -> &'static str {
751				match self {$(
752					Self::$S {..} => stringify!($S)
753				),*}
754			}
755		}
756
757		#[allow(dead_code)]
758		impl<$($C: ?Sized + $W)*> $E $(<$C>)* {
759			$(
760				#[doc = "Returns `true` if the current state is "]
761				#[doc = "[`Self::" $S "`] and `false` otherwise."]
762				pub const fn [<is_ $S:lower>](&self) -> bool {
763					matches!(self, Self::$S {..})
764				}
765			)*
766			$(
767				#[doc = "Returns `true` if the current state is "]
768				#[doc = $("[`Self::" $MT "`]")" or "* " and `false` otherwise."]
769				pub const fn [<is_ $MS:lower>](&self) -> bool {
770					$(self.[<is_ $MT:lower>]())|*
771				}
772			)*
773		}
774
775		#[doc = "A module containing a configuration- and payload-erased "]
776		#[doc = "version of [`" $E "`]."]
777		$V mod erased {
778			// import the surrounding scope for the documentation links
779			#[allow(unused_imports)]
780			use super::*;
781
782			#[cfg_attr(doc, aquamarine::aquamarine)]
783			$(#[$DOC])*
784			#[doc = "```mermaid"]
785			#[doc = "---"]
786			#[doc = "title: " $E " Transition Diagram"]
787			#[doc = "---"]
788			#[doc = "flowchart LR"]
789			#[doc = $($("\t" $S "{{" $S "}}-->" $D "{{" $D "}}\n")*)*]
790			#[doc = "```"]
791			#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
792			pub enum $E {$(
793				$(#[$SDOC])*
794				$S
795			),*}
796
797			#[allow(dead_code)]
798			impl $E {
799				/// Returns the label of the current state as a static string.
800				pub const fn label(&self) -> &'static str {
801					match self {$(
802						Self::$S => stringify!($S)
803					),*}
804				}
805
806				$(
807					#[doc = "Returns `true` if the current state is [`Self::" $S "`] and `false` otherwise."]
808					pub const fn [<is_ $S:lower>](&self) -> bool {
809						matches!(self, Self::$S)
810					}
811				)*
812
813				$(
814					#[doc = "Returns `true` if the current state is "]
815					#[doc = $("[`Self::" $MT "`]")" or "* " and `false` otherwise."]
816					pub const fn [<is_ $MS:lower>](&self) -> bool {
817						match self {
818							$(Self::$MT)|* => true,
819							_ => false
820						}
821					}
822				)*
823			}
824		}
825
826		#[doc = "A module containing weightless, branded 👻 token types that "]
827		#[doc = "are used as testimony that equally branded [`" $E "`] are in "]
828		#[doc = "the corresponding state (`" $($S)"` or `"* "`)."]
829		$V mod token {
830			use $crate::fsm::{Ephemeral, Transition};
831			use super::*;
832			use core::fmt;
833
834			#[doc = "An enumeration of token-witnesses for the states "]
835			#[doc = $("[`" $S "`](super::" $E "::" $S ")")", "* " and the meta-states "]
836			#[doc = $("[`" $MS "`]")", "* "."]
837			#[derive(Debug, Eq, PartialEq, Hash)]
838			pub enum $E<'brand> {$(
839				#[doc = "[`" $S "`] state containing the token as a witness."]
840				#[doc = ""]
841				#[doc = "[`" $S "`]: super::" $E "::" $S]
842				$S($S<'brand>)
843			),*}
844
845			#[allow(dead_code)]
846			impl<'brand> $E<'brand> {
847				/// Strips the token information from the enumeration, yielding
848				/// a type-erased variant of it.
849				pub const fn erased(&self) -> erased::$E {
850					match self {$(
851						Self::$S(_) => erased::$E::$S
852					),*}
853				}
854
855				$(
856					#[doc = "Attempts to convert the token referencing the [`" $E "`] "]
857					#[doc = "into a token referencing the more specialized [`" $S "`]."]
858					#[doc = ""]
859					#[doc = "[`" $E "`]: super::" $E]
860					#[doc = "[`" $S "`]: super::" $E "::" $S]
861					pub const fn [<as_ $S:lower>](&self) -> Option<&$S<'brand>> {
862						match self {
863							Self::$S(token) => Some(token),
864							_ => None
865						}
866					}
867
868					#[doc = "Attempts to convert the token for [`" $E "`] "]
869					#[doc = "into a token for the more specialized [`" $S "`]."]
870					#[doc = ""]
871					#[doc = "[`" $E "`]: super::" $E]
872					#[doc = "[`" $S "`]: super::" $E "::" $S]
873					pub const fn [<into_ $S:lower>](self) -> Result<$S<'brand>, error::[<Not $S>]> {
874						match self {
875							Self::$S(token) => Ok(token),
876							_ => Err(error::[<Not $S>](self.erased()))
877						}
878					}
879				)*
880
881				$(
882					#[doc = "Attempts to convert the token referencing the "]
883					#[doc = "[`" $E "`] into a token referencing state "]
884					#[doc = $($MT)" or "* "."]
885					#[doc = ""]
886					#[doc = "[`" $E  "`]: super::" $E]
887					pub const fn [<as_ $MS:lower>](&self) -> Option<&$MS<'brand>> {
888						match self {
889							$(Self::$MT(token) => Some(unsafe { &*(token as *const $MT<'brand> as *const $MS<'brand>) }),)*
890							_ => None
891						}
892					}
893
894					#[doc = "Attempts to convert the token for [`" $E "`] "]
895					#[doc = "into a token for the more specialized "$($MT)" or "* "."]
896					#[doc = ""]
897					#[doc = "[`" $E  "`]: super::" $E]
898					pub const fn [<into_ $MS:lower>](self) -> Result<$MS<'brand>, error::[<Not $MS>]> {
899						match self {
900							$(Self::$MT(token) => Ok($MS(token.0)),)*
901							_ => Err(error::[<Not $MS>](self.erased()))
902						}
903					}
904				)*
905			}
906
907			impl<'brand> From<$E<'brand>> for Ephemeral<'brand> {
908				fn from(value: $E<'brand>) -> Self {
909					match value {
910						$($E::$S(token) => token.0),*
911					}
912				}
913			}
914
915			$(
916				#[doc = "Token testifying that the equally branded "]
917				#[doc = "[`" $E "`] is [`" $S "`]."]
918				#[doc = ""]
919				#[doc = "[`" $S "`]: super::" $E "::" $S]
920				#[derive(PartialOrd, PartialEq, Ord, Eq, Hash)]
921				#[repr(transparent)]
922				$V struct $S<'brand>(pub(super) Ephemeral<'brand>);
923
924				impl<'brand> $S<'brand> {
925					/// Creates a new token of this type with an invariant lifetime `'brand`.
926					///
927					/// # Safety
928					/// Since tokens of this type are used to provide security guarantees,
929					/// creating new ones should only be done if it is absolutely certain that
930					/// the property symbolized by the token is actually upheld by the equally
931					/// branded object it applies to.
932					pub const unsafe fn new(once: Ephemeral<'brand>) -> Self {
933						Self(once)
934					}
935				}
936
937				impl<'brand> From<$S<'brand>> for Ephemeral<'brand> {
938					fn from(value: $S<'brand>) -> Self {
939						value.0
940					}
941				}
942
943				impl fmt::Debug for $S<'_> {
944					fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
945						write!(f, concat!(stringify!($E), "(", stringify!($S), ")"))
946					}
947				}
948			)*
949
950			$crate::fsm::fsm! {
951				@gen_transitions ($E) ($($W)*) ($($S $(($T))* => $($D)*),*)
952			}
953
954			$(
955				$(#[$MDOC])*
956				#[repr(transparent)]
957				$MV struct $MS<'brand>(Ephemeral<'brand>);
958
959				#[allow(dead_code)]
960				impl<'brand> $MS<'brand> {
961					/// Creates a new token of this type with an invariant lifetime `'brand`.
962					///
963					/// # Safety
964					/// Since tokens of this type are used to provide security guarantees,
965					/// creating new ones should only be done if it is absolutely certain that
966					/// the property symbolized by the token is actually upheld by the equally
967					/// branded object it applies to.
968					pub const unsafe fn new(once: Ephemeral<'brand>) -> Self {
969						Self(once)
970					}
971
972					$(
973						#[doc = "Re-casts a reference to the specialized "]
974						#[doc = "[`" $MS "`]-token into a token of the generalized "]
975						#[doc = "[`" $MB "`] kind."]
976						pub const fn [<as_ $MB:lower>](&self) -> &$MB<'brand> {
977							unsafe { &*(self as *const $MS<'brand> as *const $MB<'brand>) }
978						}
979
980						#[doc = "Converts the specialized [`" $MS "`]-token into "]
981						#[doc = "a token of the generalized [`" $MB "`] kind."]
982						pub const fn [<into_ $MB:lower>](self) -> $MB<'brand> {
983							$MB(self.0)
984						}
985					)*
986				}
987
988				impl<'brand> From<$MS<'brand>> for Ephemeral<'brand> {
989					fn from(value: $MS<'brand>) -> Self {
990						value.0
991					}
992				}
993
994				impl fmt::Debug for $MS<'_> {
995					fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
996						write!(f, concat!("Token(", stringify!($MS), ")"))
997					}
998				}
999
1000				$(
1001					#[allow(dead_code)]
1002					impl<'brand> $MT<'brand> {
1003						#[doc = "Re-casts a reference to the specialized "]
1004						#[doc = "[`" $MT "`]-token into a token of the generalized "]
1005						#[doc = "[`" $MS "`] kind."]
1006						pub const fn [<as_ $MS:lower>](&self) -> &$MS<'brand> {
1007							unsafe { &*(self as *const $MT<'brand> as *const $MS<'brand>) }
1008						}
1009
1010						#[doc = "Converts the specialized [`" $MT "`]-token into "]
1011						#[doc = "a token of the generalized [`" $MS "`] kind."]
1012						pub const fn [<into_ $MS:lower>](self) -> $MS<'brand> {
1013							$MS(self.0)
1014						}
1015					}
1016
1017					impl<'brand> From<$MT<'brand>> for $MS<'brand> {
1018						fn from(value: $MT<'brand>) -> Self { value.[<into_ $MS:lower>]() }
1019					}
1020				)*
1021			)*
1022		}
1023
1024		/// A module for error types that may occur during conversion.
1025		$V mod error {
1026			use core::fmt;
1027
1028			$(
1029				#[doc = "Error type indicating that the current [`" $E "`] was not [`" $S "`]."]
1030				#[doc = ""]
1031				#[doc = "[`" $S "`]: super::" $E "::" $S]
1032				#[doc = "[`" $E "`]: super::" $E]
1033				#[derive(Copy, Clone, Debug)]
1034				pub struct [<Not $S>](pub super::erased::$E);
1035
1036				impl fmt::Display for [<Not $S>] {
1037					fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1038						write!(
1039							f,
1040							concat!("<", stringify!($S), "> expected but <{}> found instead"),
1041							self.0.label()
1042						)
1043					}
1044				}
1045
1046				impl core::error::Error for [<Not $S>] {}
1047			)*
1048
1049			$(
1050				#[doc = "Error type indicating that the current [`" $E "`] was "]
1051				#[doc = "none of " $("[`" $MT "`](super::" $E "::" $MT ")")" or "* "."]
1052				#[doc = ""]
1053				#[doc = "[`" $E "`]: super::" $E]
1054				#[derive(Copy, Clone, Debug)]
1055				pub struct [<Not $MS>](pub super::erased::$E);
1056
1057				impl fmt::Display for [<Not $MS>] {
1058					fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1059						write!(
1060							f,
1061							concat!("either " $(, "'", stringify!($MT), "'",)" or "* " expected but state '{}' found instead"),
1062							self.0.label()
1063						)
1064					}
1065				}
1066
1067				impl core::error::Error for [<Not $MS>] {}
1068			)*
1069		}
1070	}};
1071
1072	// transitions with a generic argument
1073	(@gen_transitions ($E:ident) ($W:ty) ($($S:ident $(($T:ty))? => $($D:ident)*),*)) => {paste::paste! {$(
1074		impl<'b, C: ?Sized + $W> Transition<'b,$S<'b>, $S<'b>> for super::$E<C> {
1075			#[allow(unused_parens)]
1076			type Data = ($($T)*);
1077
1078			#[doc = "Updates the state of [`" $S "`],"]
1079			#[doc = "requiring a " $S "-token and returning it."]
1080			fn transition(this: &mut Self, token: $S<'b>, _data: Self::Data, _: $crate::fsm::Private) -> $S<'b> {
1081				*this = super::$E::$S $(({
1082					let _: $T;
1083					_data
1084				}))?;
1085				token
1086			}
1087		}
1088
1089		$(
1090			impl<'b, C: ?Sized + $W> Transition<'b,$S<'b>, $D<'b>> for super::$E<C> {
1091				type Data = <Self as Transition<'b,$D<'b>,$D<'b>>>::Data;
1092
1093				#[doc = "Performs the transition between [`" $S "`] and [`" $D "`],"]
1094				#[doc = "requiring a " $S "-token and returning the resulting "]
1095				#[doc = $D "-token."]
1096				fn transition(this: &mut Self, token: $S<'b>, data: Self::Data, p: $crate::fsm::Private) -> $D<'b> {
1097					#[cfg(feature = "tracing")]
1098					::tracing::trace!(from = %stringify!($S), into = %stringify!($D), "transition");
1099					Self::transition(this, $D(token.0), data, p)
1100				}
1101			}
1102		)*
1103	)*}};
1104
1105	// transitions without a generic argument
1106	(@gen_transitions ($E:ident) () ($($S:ident $(($T:ty))? => $($D:ident)*),*)) => {paste::paste! {$(
1107		impl<'b> Transition<'b, $S<'b>, $S<'b>> for super::$E {
1108			#[allow(unused_parens)]
1109			type Data = ($($T)*);
1110
1111			#[doc = "Updates the state of [`" $S "`],"]
1112			#[doc = "requiring a " $S "-token and returning it."]
1113			fn transition(this: &mut Self, token: $S<'b>, _data: Self::Data, _: $crate::fsm::Private) -> $S<'b> {
1114				*this = super::$E::$S $(({
1115					let _: $T;
1116					_data
1117				}))?;
1118				token
1119			}
1120		}
1121
1122		$(
1123			impl<'b> Transition<'b, $S<'b>, $D<'b>> for super::$E {
1124				type Data = <Self as Transition<'b,$D<'b>,$D<'b>>>::Data;
1125
1126				#[doc = "Performs the transition between [`" $S "`] and [`" $D "`],"]
1127				#[doc = "requiring a " $S "-token and returning the resulting "]
1128				#[doc = $D "-token."]
1129				fn transition(this: &mut Self, token: $S<'b>, data: Self::Data, p: $crate::fsm::Private) -> $D<'b> {
1130					#[cfg(feature = "tracing")]
1131					::tracing::trace!(from = %stringify!($S), into = %stringify!($D), "transition");
1132					Self::transition(this, $D(token.0), data, p)
1133				}
1134			}
1135		)*
1136	)*}};
1137}
1138
1139// Export the macro from within this module.
1140#[doc(inline)]
1141pub use __fsm as fsm;
1142
1143#[cfg(test)]
1144mod tests {
1145	use super::*;
1146
1147	// Define a state machine with three states: `Idle`, `Exec`, and `Done`.
1148	fsm! {
1149		#[allow(dead_code)]
1150		pub enum States {
1151			Idle -> { Busy },
1152			Busy -> { Idle, Done },
1153			Done(bool) -> {}
1154		}
1155	}
1156
1157	#[test]
1158	fn simple_sm() {
1159		let m1 = StateMachine::new(States::Idle);
1160
1161		let _ = m1.brand(|sm, once| {
1162			// Assert the idle state
1163			let idle = sm.token(once).into_idle().unwrap();
1164			// Transition into the running state
1165			let busy: token::Busy<'_> = sm.transition(idle, ());
1166			// Do something with the running state
1167			let _: token::Done<'_> = sm.transition(busy, true);
1168
1169			m1.borrow()
1170		});
1171	}
1172}