sfsm_proc/lib.rs
1#![doc = include_str!("../README.md")]
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::ItemFn;
6use crate::generators::{StateMachineToTokens, MessagesToTokens};
7mod generators;
8mod parsers;
9mod types;
10mod trace;
11use crate::types::{MatchStateEntry, Machine, TryMachine, Messages, State, DeriveTransition, DeriveTransitionBase};
12
13/// Generates a state machine from a given state machine definition.
14///
15/// The state machine definition is expected too hold to the following pattern:
16/// ```ignore
17/// add_state_machine!(
18/// StateMachineName,
19/// InitialState,
20/// [State1, State2, StateN, ...],
21/// [StateN => StateN, ...]
22/// );
23///```
24/// - StateMachineName: Defines the name of the state machine.
25/// - InitialState: The initial state the state machine will start with.
26/// - [State1, State2, StateN, ...]: Specifies all state structs that will be known to the state machine. Each state must implement the ``` State ``` trait.
27/// - [StateN => StateN, ...]: Defines all transitions between states that can occur. For each transition, the state must implement the according ``` Transition ``` trait.
28///
29/// An example might look like this:
30/// ```rust
31/// # use sfsm_proc::add_state_machine;
32/// # use sfsm_base::non_fallible::*;
33/// # use sfsm_base::*;
34/// # use std::marker::PhantomData;
35/// # #[derive(Debug)]
36/// # struct Ascent {}
37/// # #[derive(Debug)]
38/// # struct Descent {}
39/// # #[derive(Debug)]
40/// # struct Action<T> {
41/// # phantom: PhantomData<T>
42/// # }
43/// # impl State for Action<Ascent> { }
44/// # impl State for Action<Descent> { }
45/// #
46/// # impl Into<Action<Ascent>> for Action<Descent> {
47/// # fn into(self) -> Action<Ascent> {
48/// # todo!()
49/// # }
50/// # }
51/// # impl Transition<Action<Ascent>> for Action<Descent> {
52/// # fn guard(&self) -> TransitGuard {
53/// # todo!()
54/// # }
55/// # }
56/// #
57/// # impl Into<Action<Descent>> for Action<Ascent> {
58/// # fn into(self) -> Action<Descent> {
59/// # todo!()
60/// # }
61/// # }
62/// # impl Transition<Action<Descent>> for Action<Ascent> {
63/// # fn guard(&self) -> TransitGuard {
64/// # todo!()
65/// # }
66/// # }
67/// #
68/// add_state_machine!(
69/// #[derive(Debug)]
70/// Rocket,
71/// Action<Ascent>,
72/// [Action<Ascent>, Action<Descent>],
73/// [
74/// Action<Ascent> => Action<Descent>,
75/// Action<Descent> => Action<Ascent>
76/// ]
77/// );
78///```
79/// Expand the example to see more, or check out the examples folder for a more complete example.
80#[proc_macro]
81pub fn add_state_machine(input: TokenStream) -> TokenStream {
82
83 let definition = syn::parse_macro_input!(input as Machine);
84 let sfsm_to_tokens = StateMachineToTokens::new(&definition);
85
86 TokenStream::from(quote!{
87 #sfsm_to_tokens
88 })
89}
90
91/// Generates a fallible state machine from a given state machine definition with error handling.
92///
93/// The state machine definition is expected too hold to the following pattern:
94/// ```ignore
95/// add_fallible_state_machine!(
96/// StateMachineName,
97/// InitialState,
98/// [State1, State2, StateN, ...],
99/// [StateN => StateN, ...],
100/// ErrorType,
101/// ErrorState
102/// );
103///```
104/// - StateMachineName: Defines the name of the state machine.
105/// - InitialState: The initial state the state machine will start with.
106/// - [State1, State2, StateN, ...]: Specifies all state structs that will be known to the state machine. Each state must implement the ``` State ``` trait.
107/// - [StateN => StateN, ...]: Defines all transitions between states that can occur. For each transition, the state must implement the according ``` Transition ``` trait.
108/// - ErrorType: Defines the type of error that can be returned from the states.
109/// - ErrorState: Defines the state that will act as the error handle state. It must implement the ``` TryErrorState ``` trait. Adding it to the state definitions is optional.
110///
111/// ```rust
112/// # use sfsm_base::fallible::*;
113/// # use sfsm_proc::add_fallible_state_machine;
114/// # use sfsm_base::*;
115/// # struct Ascent {} // Ascent state
116/// # struct WaitForLaunch {} // WaitForLaunch state
117/// # // The error state
118/// # struct HandleMalfunction {}
119/// # // The error returned by all states and transitions
120/// # enum RocketMalfunction {}
121/// #
122/// # // The implementations of the states
123/// # impl TryState for Ascent {
124/// # type Error = RocketMalfunction;
125/// # }
126/// # impl TryState for WaitForLaunch {
127/// # type Error = RocketMalfunction;
128/// # }
129/// # impl TryState for HandleMalfunction {
130/// # type Error = RocketMalfunction;
131/// # }
132/// #
133/// # impl Into<WaitForLaunch> for HandleMalfunction {
134/// # fn into(self) -> WaitForLaunch {
135/// # todo!()
136/// # }
137/// # }
138/// #
139/// # impl Into<Ascent> for WaitForLaunch {
140/// # fn into(self) -> Ascent {
141/// # todo!()
142/// # }
143/// # }
144/// #
145/// # impl TryTransition<WaitForLaunch> for HandleMalfunction {
146/// # fn guard(&self) -> TransitGuard {
147/// # todo!()
148/// # }
149/// # }
150/// # impl TryTransition<Ascent> for WaitForLaunch {
151/// # fn guard(&self) -> TransitGuard {
152/// # todo!()
153/// # }
154/// # }
155/// #
156/// # impl Into<HandleMalfunction> for WaitForLaunch {
157/// # fn into(self) -> HandleMalfunction {
158/// # todo!()
159/// # }
160/// # }
161/// #
162/// # impl Into<HandleMalfunction> for Ascent {
163/// # fn into(self) -> HandleMalfunction {
164/// # todo!()
165/// # }
166/// # }
167/// #
168/// # // The TryErrorState implementation for the error state
169/// # impl TryErrorState for HandleMalfunction {
170/// # fn consume_error(&mut self, err: Self::Error) {
171/// # // Do something with the error
172/// # }
173/// # }
174/// #
175/// add_fallible_state_machine!(
176/// Rocket,
177/// WaitForLaunch,
178/// [WaitForLaunch, Ascent, HandleMalfunction],
179/// [
180/// WaitForLaunch => Ascent,
181/// HandleMalfunction => WaitForLaunch
182/// ],
183/// RocketMalfunction,
184/// HandleMalfunction
185/// );
186///```
187/// Expand the example to see more, or check out the examples folder for a more complete example.
188#[proc_macro]
189pub fn add_fallible_state_machine(input: TokenStream) -> TokenStream {
190
191 let definition = syn::parse_macro_input!(input as TryMachine);
192 let sfsm_to_tokens = StateMachineToTokens::new(&definition.state_machine);
193
194 TokenStream::from(quote!{
195 #sfsm_to_tokens
196 })
197}
198
199/// Generates code to push messages into states or poll messages from states.
200///
201/// The messaging definition is expected too hold to the following pattern:
202/// ```ignore
203/// add_messages!(
204/// StateMachineName,
205/// [
206/// Message1 <- State1,
207/// Message2 <- State1,
208/// Message1 -> State2,
209/// ...
210/// ]
211/// );
212/// ```
213/// - StateMachineName: This must match a previously with add_state_machine defined state machine.
214/// - [ Message1 <- State1, ... ] Defines all messages that can be passed back an forth. The message specifies the struct/enum that will be used as a message, the <- arrow defines a poll and the -> a push and the state is the target or source state.
215/// For each message, the source/target state must implement the according ``` ReceiveMessage ``` or ``` ReturnMessage ``` trait.
216/// An example might look like this.
217/// ```rust
218/// # use sfsm_proc::add_state_machine;
219/// # use sfsm_proc::add_messages;
220/// # use sfsm_base::*;
221/// # use std::marker::PhantomData;
222/// # #[derive(Debug)]
223/// # struct Launch {}
224/// # #[derive(Debug)]
225/// # struct Land {}
226/// # struct Ascent {}
227/// # struct Descent {}
228/// # struct Action<T> {
229/// # phantom: PhantomData<T>
230/// # }
231/// # #[derive(Debug)]
232/// # struct Command<T> {
233/// # phantom: PhantomData<T>
234/// # }
235/// # struct Status { height: f32, speed: f32}
236/// # impl State for Action<Ascent> { }
237/// # impl State for Action<Descent> { }
238/// #
239/// # impl ReceiveMessage<Command<Launch>> for Action<Descent> {
240/// # fn receive_message(&mut self, message: Command<Launch>) {
241/// # println!("Received message {:?}", message);
242/// # }
243/// # }
244/// #
245/// # impl ReceiveMessage<Command<Land>> for Action<Ascent> {
246/// # fn receive_message(&mut self, message: Command<Land>) {
247/// # println!("Received message {:?}", message);
248/// # }
249/// # }
250///#
251/// # impl ReturnMessage<Status> for Action<Ascent> {
252/// # fn return_message(&mut self) -> Option<Status> {
253/// # return Some(Status { height: 1.0f32, speed: 2.0f32 });
254/// # }
255/// # }
256/// #
257/// # impl ReturnMessage<Status> for Action<Descent> {
258/// # fn return_message(&mut self) -> Option<Status> {
259/// # return Some(Status { height: 1.0f32, speed: 2.0f32 });
260/// # }
261/// # }
262/// #
263/// # add_state_machine!(
264/// # Rocket,
265/// # Action<Ascent>,
266/// # [Action<Descent>, Action<Ascent>],
267/// # []
268/// # );
269/// #
270/// add_messages!(
271/// Rocket,
272/// [
273/// Command<Launch> -> Action<Descent>,
274/// Command<Land> -> Action<Ascent>,
275/// Status <- Action<Ascent>,
276/// Status <- Action<Descent>
277/// ]
278/// );
279///```
280#[proc_macro]
281pub fn add_messages(input: TokenStream) -> TokenStream {
282
283 let definition = syn::parse_macro_input!(input as Messages);
284 let messages_to_tokens = MessagesToTokens::new(&definition);
285
286 TokenStream::from(quote!{
287 #messages_to_tokens
288 })
289}
290
291/// Generate the enum entry of a state. Expects the name of the sfsm and the name (and type args)
292/// of the state as well as the desired name of the variable to work with as arguments.
293/// Can be used to generate match branches for example.
294/// ```ignore
295/// match exit {
296/// match_state_entry!(NameOfTheSfsm, DesiredState<AndType>, var_name) => {
297/// // Access "var_name" here.
298/// // Var name will be Option<DesiredState<AndType>>
299/// },
300/// _ => {
301/// }
302/// }
303/// ```
304#[proc_macro]
305pub fn match_state_entry(input: TokenStream) -> TokenStream {
306
307 let match_state_entry: MatchStateEntry = syn::parse_macro_input!(input as MatchStateEntry);
308 let state_entry = match_state_entry.state_entry;
309 let enum_name = state_entry.enum_name;
310 let state_entry = state_entry.state_entry;
311 let var_name = match_state_entry.var_name;
312
313 TokenStream::from(quote!{
314 #enum_name::#state_entry(#var_name)
315 })
316}
317
318/// Creates a wrapper around a log function to forward the logs to.
319/// With the help of ``` sfsm_trace ```, a logger function to which all logs from the state machine
320/// are forwarded to can be configured
321/// ```ignore
322/// #[sfsm_trace]
323/// fn trace(log: &str) {
324/// println!("{}", log);
325/// }
326/// ```
327#[proc_macro_attribute]
328pub fn sfsm_trace(_attr: TokenStream, item: TokenStream) -> TokenStream {
329 let trace_function: ItemFn = syn::parse_macro_input!(item as ItemFn);
330 let trace_function_ident: &proc_macro2::Ident = &trace_function.sig.ident;
331 TokenStream::from(quote!{
332 #trace_function
333 fn __sfsm_trace(str: &str) {
334 #trace_function_ident(str);
335 }
336 })
337}
338
339/// Derives an empty transition of a transition from one state into another and allows to
340/// customise if it should always transit or never.
341/// ```ignore
342/// derive_transition!(Foo, Bar, TransitGuard::Transit);
343/// // Generates
344/// impl Transition<Bar> for Bar {
345/// fn guard(&self) -> TransitGuard {
346/// TransitGuard::Transit
347/// }
348/// }
349/// ```
350/// This macro is implemented as a proc macro instead of a derive macro because it needs additional
351/// info that is difficult to get into a derive macro in a semantic way.
352#[proc_macro]
353pub fn derive_transition(input: TokenStream) -> TokenStream {
354 let transition: DeriveTransition = syn::parse_macro_input!(input as DeriveTransition);
355 let src = transition.transition.src;
356 let dst = transition.transition.dst;
357 let guard = transition.guard;
358 TokenStream::from(quote!{
359 impl Transition<#dst> for #src {
360 fn guard(&self) -> TransitGuard {
361 #guard
362 }
363 }
364 })
365}
366
367/// Derives an empty implementation of the state.
368/// ```ignore
369/// derive_state!(Foo);
370/// // Generates
371/// impl State for Foo {};
372/// ```
373/// It's somewhat redundant, but it is added for consistency to match the other derive_* functions
374/// and to help keep state machine short and clean.
375#[proc_macro]
376pub fn derive_state(input: TokenStream) -> TokenStream {
377 let state: State = syn::parse_macro_input!(input as State);
378 let name = state.name;
379 let generics = state.generics;
380 TokenStream::from(quote!{
381 impl State for #name #generics {}
382 })
383}
384
385/// Derives an empty transition of a transition from one state into another and allows to
386/// customise if it should always transit or never.
387/// ```ignore
388/// derive_try_transition!(Foo, Bar, TransitGuard::Transit);
389/// // Generates
390/// impl TryTransition<Bar> for Bar {
391/// fn guard(&self) -> TransitGuard {
392/// TransitGuard::Transit
393/// }
394/// }
395/// ```
396/// This macro is implemented as a proc macro instead of a derive macro because it needs additional
397/// info that is difficult to get into a derive macro in a semantic way.
398#[proc_macro]
399pub fn derive_try_transition(input: TokenStream) -> TokenStream {
400 let transition: DeriveTransition = syn::parse_macro_input!(input as DeriveTransition);
401 let src = transition.transition.src;
402 let dst = transition.transition.dst;
403 let guard = transition.guard;
404 TokenStream::from(quote!{
405 impl TryTransition<#dst> for #src {
406 fn guard(&self) -> TransitGuard {
407 #guard
408 }
409 }
410 })
411}
412
413/// Derives an empty implementation of the TryState.
414/// ```ignore
415/// derive_try_state!(Foo);
416/// // Generates
417/// impl TryState for Foo {};
418/// ```
419/// It's somewhat redundant, but it is added for consistency to match the other derive_* functions
420/// and to help keep state machine short and clean.
421#[proc_macro]
422pub fn derive_try_state(input: TokenStream) -> TokenStream {
423 let state: State = syn::parse_macro_input!(input as State);
424 let name = state.name;
425 let generics = state.generics;
426 TokenStream::from(quote!{
427 impl TryState for #name #generics {}
428 })
429}
430
431
432/// Derives an a implementation of the into trait for the transition if the target state does
433/// not contains any members
434/// ```ignore
435/// derive_transition_into!(Foo, Bar);
436/// // Generates
437/// impl Into<Bar> for Foo {
438/// fn into(self) -> Bar {
439/// Bar {}
440/// }
441/// }
442/// ```
443/// This macro is implemented as a proc macro instead of a derive macro because it needs additional
444/// info that is difficult to get into a derive macro in a semantic way.
445#[proc_macro]
446pub fn derive_transition_into(input: TokenStream) -> TokenStream {
447 let transition: DeriveTransitionBase = syn::parse_macro_input!(input as DeriveTransitionBase);
448 let src = transition.src;
449 let dst = transition.dst;
450 TokenStream::from(quote!{
451 impl Into<#dst> for #src {
452 fn into(self) -> #dst {
453 #dst {}
454 }
455 }
456 })
457}
458
459/// Derives an empty a implementation fo the into trait for the transition if the target state
460/// implements the ``` Default ``` trait.
461/// ```ignore
462/// derive_transition_into_default!(Foo, Bar);
463/// // Generates
464/// impl Into<Bar> for Foo {
465/// fn into(self) -> Bar {
466/// Bar::default()
467/// }
468/// }
469/// ```
470/// This macro is implemented as a proc macro instead of a derive macro because it needs additional
471/// info that is difficult to get into a derive macro in a semantic way.
472#[proc_macro]
473pub fn derive_transition_into_default(input: TokenStream) -> TokenStream {
474 let transition: DeriveTransitionBase = syn::parse_macro_input!(input as DeriveTransitionBase);
475 let src = transition.src;
476 let dst = transition.dst;
477 TokenStream::from(quote!{
478 impl Into<#dst> for #src {
479 fn into(self) -> #dst {
480 #dst::default()
481 }
482 }
483 })
484}