| Crates.io | phyto-fsm |
| lib.rs | phyto-fsm |
| version | 0.2.1 |
| created_at | 2025-08-20 07:00:09.30957+00 |
| updated_at | 2026-01-12 22:20:59.378704+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 | 158,739 |
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.
| Feature | Description | Example |
|---|---|---|
| Events with custom data | Trigger transitions with typed event parameters | actions.rs |
| Custom data types | Use any type (primitives, structs, references, pointers) as event data | data_types.rs |
| Actions on transitions | Execute custom code when transitions occur | actions.rs |
| Composite states | Nested/hierarchical states with automatic enter state resolution | composite_states.rs |
| Substate-to-substate transitions | Transitions between substates across different parent states | substate_to_substate.rs |
| Self-transitions | States that transition to themselves | transitions.rs |
| Alternative transitions | Multiple transitions from the same state with different events | transitions.rs |
| Transition logging | Optional logging via log crate | four_seasons.rs |
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 |
| 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 | Method name | snake_case 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 Freezing
state Mild
[*] --> Freezing
Freezing -> Mild: TemperatureRises
Mild -> Freezing: TemperatureDrops
}
state Spring {
state Chilly
state Warm
[*] --> Chilly
Chilly -> Warm: TemperatureRises
Warm -> Chilly: TemperatureDrops
}
[*] --> Winter
Winter --> Spring : TimeAdvances
Spring --> Summer : TimeAdvances / StartBlooming
Summer --> Autumn : TimeAdvances / RipenFruit
Autumn --> Winter : TimeAdvances / DropPetals
@enduml
use phyto_fsm::generate_fsm;
// Generate FSM from PlantUML file
generate_fsm!(
file_path = "path/to/your/diagram.puml",
log_level = "debug" // Optional: enables transition logging
);
use plant_fsm::{IPlantFsmActions, IPlantFsmEventParams, NoEventData};
struct PlantActions;
impl IPlantFsmEventParams for PlantActions {
type TimeAdvancesParams = std::time::SystemTime;
type TemperatureRisesParams = NoEventData;
type TemperatureDropsParams = NoEventData;
}
impl IPlantFsmActions for PlantActions {
fn start_blooming(&mut self, time: Self::TimeAdvancesParams) {
println!("Started blooming at {:?}", time);
}
fn ripen_fruit(&mut self, _: Self::TimeAdvancesParams) {
println!("Fruit is ripening");
}
fn drop_petals(&mut self, _: Self::TimeAdvancesParams) {
println!("Dropping petals");
}
}
use plant_fsm::PlantFsm;
fn main() {
let actions = PlantActions;
let mut fsm = PlantFsm::new(actions);
// Transition within Winter: Freezing -> Mild
fsm.temperature_rises(());
// Transition to next season: Winter -> Spring (enters Spring::Chilly)
fsm.time_advances(std::time::SystemTime::now());
}