magicstatemachines/contract.rs
1use core::pin::Pin;
2#[cfg(feature = "unique-rc-arc")]
3use std::rc::UniqueRc;
4#[cfg(feature = "unique-rc-arc")]
5use std::sync::UniqueArc;
6
7/// Declares that a definition crate permits `TState` as an initial state.
8///
9/// This trait is normally emitted by
10/// [`StateMachineDefinition!`](macro@crate::StateMachineDefinition). It is the
11/// proof used by [`State::new`](crate::State::new),
12/// [`StateOwned::new`](crate::StateOwned::new), and shared-state constructors:
13/// creating a fresh state token only compiles for states declared in the
14/// definition crate.
15///
16/// ```ignore
17/// pub struct ConnectionStandin;
18/// pub struct Disconnected;
19///
20/// impl magicstatemachines::Initial<Disconnected> for ConnectionStandin {}
21/// ```
22///
23/// Most users should not write that impl manually; prefer:
24///
25/// ```ignore
26/// magicstatemachines::StateMachineDefinition! {
27/// for ConnectionStandin;
28///
29/// Initial: Disconnected;
30/// }
31/// ```
32pub trait Initial<TState> {}
33
34/// Declares that a definition crate permits `TFrom -> TTo`.
35///
36/// The definition crate owns the stand-in and state types. Rust's orphan rules
37/// therefore prevent an implementation crate from adding transitions. This
38/// trait is the graph edge only; it does not define what happens to the
39/// runtime value during the edge. Runtime effects are supplied later by
40/// [`StateMachineImpl!`](macro@crate::StateMachineImpl).
41///
42/// `F` is the required positional call signature for the transition. A
43/// zero-argument transition can use the default `fn()`. A transition that must
44/// be called with a `String` declares `type F = fn(String)`.
45///
46/// ```ignore
47/// pub struct ConnectionStandin;
48/// pub struct Connected;
49/// pub struct Authenticated;
50///
51/// impl magicstatemachines::Transition<Connected, Authenticated> for ConnectionStandin {
52/// type F = fn(String);
53/// }
54/// ```
55///
56/// With the definition macro, the same declaration is usually written as:
57///
58/// ```ignore
59/// magicstatemachines::StateMachineDefinition! {
60/// for ConnectionStandin;
61///
62/// Initial: Connected;
63/// transition Connected => Authenticated(user: String);
64/// }
65/// ```
66///
67/// The name `user` is documentation for the contract and for the matching
68/// implementation body. The actual transition call remains positional:
69/// `transition!(self, user.into())`.
70pub trait Transition<TFrom, TTo> {
71 /// Function signature required to perform this transition.
72 type F = fn();
73}
74
75/// Stable proof that a transition signature accepts a tuple of arguments.
76#[doc(hidden)]
77pub trait TransitionSignature<Args> {}
78
79impl TransitionSignature<()> for fn() {}
80
81macro_rules! transition_signature_impls {
82 ($(($($arg:ident),+)),* $(,)?) => {
83 $(
84 impl<$($arg),+> TransitionSignature<($($arg,)+)> for fn($($arg),+) {}
85 )*
86 };
87}
88
89transition_signature_impls! {
90 (A),
91 (A, B),
92 (A, B, C),
93 (A, B, C, D),
94 (A, B, C, D, E),
95 (A, B, C, D, E, F),
96 (A, B, C, D, E, F, G),
97 (A, B, C, D, E, F, G, H),
98 (A, B, C, D, E, F, G, H, I),
99 (A, B, C, D, E, F, G, H, I, J),
100 (A, B, C, D, E, F, G, H, I, J, K),
101 (A, B, C, D, E, F, G, H, I, J, K, L),
102}
103
104/// Connects an implementation type to a state-machine definition.
105///
106/// Implementations are `'static` so storage backends can provide borrowed
107/// guard families without repeating the implementation type in the backend.
108/// The associated `Standin` selects the definition-crate contract, while
109/// `TransitionToken` is the private capability required to perform retagging.
110///
111/// [`crate::StateMachineImpl!`] generates this implementation and keeps the
112/// transition capability's construction private. Code outside the invocation
113/// module cannot manufacture the token:
114///
115/// ```compile_fail
116/// use magicstatemachines::{Initial, State, StateMachineImpl, States, StorageStateOwned, Transition};
117///
118/// mod implementation {
119/// use super::*;
120///
121/// pub struct Standin;
122/// pub struct Runtime;
123/// States! {
124/// Ready;
125/// Running;
126/// }
127/// impl Initial<Ready> for Standin {}
128/// impl Transition<Ready, Running> for Standin {}
129///
130/// magicstatemachines::StateMachineImpl!(Runtime: Standin; transition Ready => Running(););
131///
132/// pub fn ready() -> State<StorageStateOwned, Runtime, Ready> {
133/// State::new(Runtime)
134/// }
135/// }
136///
137/// let ready = implementation::ready();
138/// let _ = magicstatemachines::transition_state::<_, _, _, implementation::Running>(
139/// ready,
140/// implementation::__StateMachineTransitionToken(())
141/// ).call(());
142/// ```
143///
144/// The generated ergonomic helpers are also private to the invocation module.
145/// This means implementation methods can call [`transition!`](macro@crate::transition),
146/// but external callers can only call the methods you expose:
147///
148/// ```compile_fail
149/// use magicstatemachines::{Initial, State, States, StorageStateOwned, Transition};
150///
151/// mod implementation {
152/// use super::*;
153///
154/// pub struct Standin;
155/// pub struct Runtime;
156/// States! {
157/// Ready;
158/// Running;
159/// }
160///
161/// impl Initial<Ready> for Standin {}
162/// impl Transition<Ready, Running> for Standin {}
163/// magicstatemachines::StateMachineImpl!(Runtime: Standin; transition Ready => Running(););
164///
165/// pub fn ready() -> State<StorageStateOwned, Runtime, Ready> {
166/// State::new(Runtime)
167/// }
168/// }
169///
170/// let ready = implementation::ready();
171/// let _ = magicstatemachines::transition!(ready);
172/// ```
173pub trait StateMachineImpl: 'static {
174 /// Definition-crate ZST used to select the state-machine contract.
175 type Standin;
176
177 /// Runtime implementation controlled by the state machine.
178 type Impl: StateMachineImpl<Standin = Self::Standin, Impl = Self::Impl>;
179
180 /// Capability required to perform transitions.
181 ///
182 /// Use [`crate::StateMachineImpl!`] to generate this capability and its
183 /// private ergonomic transition helpers.
184 type TransitionToken;
185}
186
187impl<T> StateMachineImpl for Box<T>
188where
189 T: StateMachineImpl + ?Sized,
190{
191 type Standin = T::Standin;
192 type Impl = T::Impl;
193 type TransitionToken = T::TransitionToken;
194}
195
196#[cfg(feature = "unique-rc-arc")]
197impl<T> StateMachineImpl for UniqueRc<T>
198where
199 T: StateMachineImpl + ?Sized,
200{
201 type Standin = T::Standin;
202 type Impl = T::Impl;
203 type TransitionToken = T::TransitionToken;
204}
205
206#[cfg(feature = "unique-rc-arc")]
207impl<T> StateMachineImpl for UniqueArc<T>
208where
209 T: StateMachineImpl + ?Sized,
210{
211 type Standin = T::Standin;
212 type Impl = T::Impl;
213 type TransitionToken = T::TransitionToken;
214}
215
216impl<T> StateMachineImpl for Pin<Box<T>>
217where
218 T: StateMachineImpl + ?Sized,
219{
220 type Standin = T::Standin;
221 type Impl = T::Impl;
222 type TransitionToken = T::TransitionToken;
223}