| Crates.io | phyto-fsm |
| lib.rs | phyto-fsm |
| version | 0.1.0 |
| created_at | 2025-08-20 07:00:09.30957+00 |
| updated_at | 2025-08-20 07:00:09.30957+00 |
| description | A Rust procedural macro for generating type-safe state machines from PlantUML diagrams |
| homepage | |
| repository | https://github.com/TobTheRock/phytofsm |
| max_upload_size | |
| id | 1802943 |
| size | 87,724 |
A Rust procedural macro for generating state machines from PlantUML diagrams.
Many state machine frameworks provide the possibility to create a visualization from your FSM
defined by the source code, which then can be e.g. added to your documentation.
phyto-fsm does it the opposite way:
phyto-fsm
This way the design of the FSM is easy to grasp first hand and documentation and source code are always inline.
Missing:
When you use generate_fsm!("path/to/diagram.puml"), the macro generates various traits, enums, and structs based on your PlantUML diagram name and elements. Here's how they are named:
For a PlantUML diagram whose name is given by:
@startuml DiagramName
...
@enduml
The following is generated:
| Generated Item | Naming Pattern | Description |
|---|---|---|
| FSM Struct | {DiagramName} |
Main state machine struct (UpperCamelCase) |
| Event Parameters Trait | I{DiagramName}EventParams |
Trait defining event parameter types |
| Actions Trait | I{DiagramName}Actions |
Trait defining action methods |
| Event Enum | {DiagramName}Event |
Enum containing all possible events |
| State Struct | {DiagramName}State |
Internal state representation |
| Module | {diagram_name} |
Generated module name (snake_case) |
The actions and events associated with a transition must be defined as such with PlantUML:
StateA --> StateB : EventName / ActionName
/ to separate the event from the action| PlantUML Element | Generated Item | Naming Pattern |
|---|---|---|
| Event | Event enum variant | UpperCamelCase of event name |
| Event | Parameter type | {EventName}Params |
| Action | Method name | snake_case of action name |
| State | State name | Preserved as written in PlantUML |
| State | Function name | snake_case of state name |
@startuml PlantFsm
state Winter
state Spring
state Summer
state Autumn
[*] --> Winter
Winter --> Spring : TemperatureRises
Spring --> Summer : DaylightIncreases / StartBlooming
Summer --> Autumn : DaylightDecreases / RipenFruit
Autumn --> Winter : TemperatureDrops / DropPetals
@enduml
use phyto_fsm::generate_fsm;
// Generate FSM from PlantUML file
generate_fsm!("path/to/your/diagram.puml");
use plant_fsm::{IPlantFsmActions, IPlantFsmEventParams, NoEventData};
struct PlantActions;
impl IPlantFsmEventParams for PlantActions {
type TemperatureRisesParams = NoEventData;
type DaylightIncreasesParams = i32; // Lumens
type DaylightDecreasesParams = NoEventData;
type TemperatureDropsParams = NoEventData;
}
impl IPlantFsmActions for PlantActions {
fn start_blooming(&mut self, lumens: Self::DaylightIncreasesParams) {
println!("🌸 Blooming with {} lumens!", lumens);
}
fn ripen_fruit(&mut self, _: Self::DaylightDecreasesParams) {
println!("🍎 Fruit is ripening");
}
fn drop_petals(&mut self, _: Self::TemperatureDropsParams) {
println!("🍂 Dropping petals");
}
}
use plant_fsm::{PlantFsm, PlantFsmEvent};
fn main() {
let actions = PlantActions;
let mut fsm = PlantFsm::new(actions);
fsm.trigger_event(PlantFsmEvent::TemperatureRises(()));
fsm.trigger_event(PlantFsmEvent::DaylightIncreases(1000));
fsm.trigger_event(PlantFsmEvent::DaylightDecreases(()));
fsm.trigger_event(PlantFsmEvent::TemperatureDrops(()));
}