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}