phyto_fsm/lib.rs
1use proc_macro::TokenStream;
2use quote::quote;
3
4mod codegen;
5mod error;
6mod file;
7mod logging;
8mod options;
9mod parser;
10#[cfg(test)]
11mod test;
12
13use crate::codegen::FsmCodeGenerator;
14
15#[proc_macro]
16/// Parse the given FSM definition file and generate the corresponding Rust code.
17///
18/// The input to this macro is the path a file containing the FSM definition.
19/// Currently, only PlantUML state machine diagrams are supported.
20/// This will generate an FSM implementation and traits for events and actions, which the use has
21/// to implement.
22///
23/// # Syntax
24/// ```ignore
25/// // With default parameters:
26/// #generate_fsm!("path/to/fsm_definition.puml")
27/// // With parameters:
28/// #generate_fsm!(file_path = "path/to/fsm_definition.puml", log_level = "debug")
29///
30/// # Parameters
31///
32/// | Parameter | Description | Default
33/// |-----------|-------------|----------
34/// | **file_path** | Path to the FSM definition file. This parameter is required. | None
35/// | **log_level** | Optional log level for state transitions. Possible values: `error`, `warn`, `info`, `debug`, `trace`. If not set, no logging is performed. | None
36///
37///
38/// ```
39/// # Generated Code
40///
41/// | Generated Item | Naming Pattern | Description |
42/// |---------------|----------------|-------------|
43/// | **FSM Struct** | `{DiagramName}` | Main state machine struct (UpperCamelCase) |
44/// | **Event Parameters Trait** | `I{DiagramName}EventParams` | Trait defining event parameter types |
45/// | **Actions Trait** | `I{DiagramName}Actions` | Trait defining action methods |
46/// | **Event Enum** | `{DiagramName}Event` | Enum containing all possible events |
47/// | **State Struct** | `{DiagramName}State` | Internal state representation |
48/// | **Module** | `{diagram_name}` | Generated module name (snake_case) |
49///
50/// # Example
51///
52/// ```rust,ignore
53/// use phyto_fsm::generate_fsm;
54/// generate_fsm!("path/to/fsm_definition.puml");
55///
56/// use my_fsm::*; // Import generated module
57///
58/// struct MyActions;
59/// impl IMyFsmActions for MyActions {
60/// fn some_action(&mut self, params: SomeEventParams) {
61/// // Implement action logic here
62/// }
63/// // Implement other actions...
64/// }
65///
66/// impl IMyFsmEventParams for MyActions {
67/// type SomeEventParams = NoEventData;
68/// type OtherEventParams = String;
69/// // Define other event parameter types...
70/// }
71///
72/// let actions = MyActions;
73/// let mut fsm = MyFsm::new(actions);
74/// fsm.trigger_event(MyFsmEvent::SomeEvent(()));
75/// fsm.trigger_event(MyFsmEvent::OtherEvent("data".to_string()));
76/// ```
77pub fn generate_fsm(input: TokenStream) -> TokenStream {
78 match generate_fsm_inner(input) {
79 Ok(tokens) => tokens,
80 Err(error) => {
81 let error_msg = format!("[phyto-fsm] {}", error);
82 quote! {
83 compile_error!(#error_msg);
84 }
85 .into()
86 }
87 }
88}
89
90fn generate_fsm_inner(input: TokenStream) -> error::Result<TokenStream> {
91 logging::init();
92
93 let options: options::Options =
94 syn::parse(input).map_err(|e| error::Error::InvalidInput(e.to_string()))?;
95 let file_path = file::FilePath::resolve(&options.file_path, proc_macro::Span::call_site());
96 let file = file::FsmFile::try_open(file_path)?;
97 let parsed_fsm = parser::ParsedFsm::try_parse(file.content())?;
98 let generator = FsmCodeGenerator::new(&options.codegen);
99 let fsm_code = generator.generate(parsed_fsm);
100
101 Ok(fsm_code.into())
102}