This document provides comprehensive API documentation for the Rust statechart library.
- Core Concepts
- Builder API
- Model Types
- Runtime API
- Validation API
- Visualization API
- Error Handling
- Examples
A state machine is defined using the builder pattern and consists of:
- States: The possible conditions or situations
- Events: Triggers that cause transitions
- Transitions: Rules for moving between states
- Guards: Conditions that must be met for transitions
- Actions: Code executed during transitions
- Context: Shared data accessible throughout the machine
All user types must implement these core traits:
use statechart::prelude::*;
// State identifiers
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum MyState { Idle, Running, Stopped }
// Event identifiers
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum MyEvent { Start, Stop, Reset }
// Context for shared data
#[derive(Debug)]
struct MyContext {
counter: u32,
name: String,
}The main entry point for creating state machines.
use statechart::builder::StateMachineBuilder;
let definition = StateMachineBuilder::<MyState, MyEvent, MyContext>::new()
.initial_state(MyState::Idle)
.add_simple_state(MyState::Idle)
.add_simple_state(MyState::Running)
.add_simple_state(MyState::Stopped)
.add_transition(
MyState::Idle, // source
MyState::Running, // target
MyEvent::Start, // event
None, // guard (optional)
vec![], // actions
)
.build()?;new() -> Self- Create a new builderinitial_state(state: S) -> Self- Set the initial stateadd_simple_state(state: S) -> Self- Add a simple stateadd_state(state: State<S, C>) -> Self- Add a complex stateadd_transition(source: S, target: S, event: E, guard: Option<Box<dyn Guard<C>>>, actions: Vec<Box<dyn Action<C>>>) -> Self- Add a transitionbuild() -> Result<StateMachineDefinition<S, E, C>, ValidationError>- Build and validate the definition
For creating complex states with entry/exit actions and hierarchical structure.
use statechart::builder::StateBuilder;
let composite_state = StateBuilder::<MyState, MyEvent, MyContext>::new(MyState::Running)
.composite()
.initial(MyState::SubState1)
.on_entry(vec![Box::new(|ctx| {
println!("Entering running state");
Ok(())
})])
.on_exit(vec![Box::new(|ctx| {
println!("Exiting running state");
Ok(())
})])
.build();new(id: S) -> Self- Create a new state buildersimple() -> Self- Make this a simple state (default)composite() -> Self- Make this a composite stateparallel() -> Self- Make this a parallel statefinal_state() -> Self- Make this a final stateinitial(state: S) -> Self- Set initial substate for composite stateson_entry(actions: Vec<Box<dyn Action<C>>>) -> Self- Add entry actionson_exit(actions: Vec<Box<dyn Action<C>>>) -> Self- Add exit actionsbuild() -> State<S, C>- Build the state
Represents a state in the state machine.
use statechart::model::State;
// Create states
let simple = State::new(MyState::Idle);
let composite = State::new(MyState::Running)
.with_kind(StateKind::Composite)
.with_initial_state(MyState::SubState1);Wrapper for events with optional payload and metadata.
use statechart::model::Event;
// Simple event
let event = Event::new(MyEvent::Start);
// Event with payload
let event_with_data = Event::new(MyEvent::DataReceived)
.with_payload("Hello, World!".to_string());
// Access payload
if let Ok(data) = event_with_data.payload::<String>() {
println!("Received: {}", data);
}Defines how to move between states.
use statechart::model::Transition;
let transition = Transition::new(
MyState::Idle,
MyState::Running,
MyEvent::Start,
)
.with_guard(Box::new(|ctx: &MyContext| ctx.counter > 0))
.with_action(Box::new(|ctx: &mut MyContext| {
ctx.counter += 1;
println!("Started! Counter: {}", ctx.counter);
Ok(())
}));Conditions that must be met for transitions to occur.
use statechart::model::Guard;
// Closure guard
let guard = Box::new(|ctx: &MyContext| ctx.counter < 10);
// Named guard for debugging
let named_guard = NamedGuard::new(
"counter_check",
Box::new(|ctx: &MyContext| ctx.counter < 10)
);
// Composite guards
let and_guard = AndGuard::new(vec![guard1, guard2]);
let or_guard = OrGuard::new(vec![guard1, guard2]);
let not_guard = NotGuard::new(guard);Code executed during transitions or state entry/exit.
use statechart::model::Action;
// Closure action
let action = Box::new(|ctx: &mut MyContext| {
ctx.counter += 1;
println!("Counter incremented to {}", ctx.counter);
Ok(())
});
// Named action for debugging
let named_action = NamedAction::new(
"increment_counter",
Box::new(|ctx: &mut MyContext| {
ctx.counter += 1;
Ok(())
})
);The runtime state machine that processes events.
use statechart::runtime::StateMachine;
// Create machine
let context = MyContext { counter: 0, name: "Test".to_string() };
let mut machine = StateMachine::new(definition, context)?;
// Process events
machine.process_event(Event::new(MyEvent::Start))?;
// Check current state
if machine.is_in_state(&MyState::Running) {
println!("Machine is running");
}
// Access context
let counter = machine.context().counter;new(definition: StateMachineDefinition<S, E, C>, context: C) -> Result<Self, StateMachineError>- Create new machineprocess_event(&mut self, event: Event<E>) -> Result<bool, StateMachineError>- Process an eventqueue_event(&mut self, event: Event<E>)- Queue an event for later processingprocess_queued_events(&mut self) -> Result<Vec<bool>, StateMachineError>- Process all queued eventscurrent_state(&self) -> &S- Get current stateis_in_state(&self, state: &S) -> bool- Check if in specific stateis_final(&self) -> bool- Check if in final statecontext(&self) -> &C- Get context referencecontext_mut(&mut self) -> &mut C- Get mutable context reference
Validates state machine definitions for correctness.
use statechart::validation::{validate, Validator};
// Quick validation
let report = validate(&definition);
if !report.is_valid() {
for issue in report.issues() {
println!("Validation issue: {}", issue.message());
}
}
// Custom validation
let mut validator = Validator::new();
let report = validator.validate(&definition);Analyze state connectivity and reachability.
use statechart::validation::reachability::*;
// Find reachable states
let reachable = find_reachable_states(&definition);
// Find unreachable states
let unreachable = find_unreachable_states(&definition);
// Check if specific state is reachable
let can_reach = is_reachable(&definition, &MyState::Idle, &MyState::Running);
// Find states that can reach a target
let reaching = find_states_reaching(&definition, &MyState::Stopped);
// Full connectivity analysis
let analysis = analyze_connectivity(&definition);Export state machines to Graphviz DOT format for visualization.
use statechart::visualization::{to_dot, to_dot_with_options, DotOptions};
// Basic DOT export
let dot = to_dot(&definition);
println!("{}", dot);
// Customized export
let options = DotOptions::new()
.with_title("My State Machine")
.with_state_style(StateStyle::Rounded)
.with_transition_style(TransitionStyle::Curved);
let dot = to_dot_with_options(&definition, &options);The library uses a comprehensive error system:
use statechart::error::*;
match machine.process_event(event) {
Ok(transitioned) => {
if transitioned {
println!("Transition occurred");
}
}
Err(StateMachineError::InvalidTransition { from, event }) => {
println!("No transition from {:?} on {:?}", from, event);
}
Err(StateMachineError::ActionFailed { action, source }) => {
println!("Action '{}' failed: {}", action, source);
}
Err(e) => {
println!("Other error: {}", e);
}
}StateMachineError- Runtime errorsValidationError- Definition validation errorsActionError- Action execution errorsBuilderError- Builder configuration errors
use statechart::prelude::*;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum State { Off, On }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum Event { Toggle }
let definition = StateMachineBuilder::<State, Event, ()>::new()
.initial_state(State::Off)
.add_simple_state(State::Off)
.add_simple_state(State::On)
.add_transition(State::Off, State::On, Event::Toggle, None, vec![])
.add_transition(State::On, State::Off, Event::Toggle, None, vec![])
.build()?;
let mut machine = StateMachine::new(definition, ())?;
assert_eq!(machine.current_state(), &State::Off);
machine.process_event(Event::new(Event::Toggle))?;
assert_eq!(machine.current_state(), &State::On);use statechart::prelude::*;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum State { Idle, Working }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum Event { Start, Finish }
#[derive(Debug)]
struct Context {
work_count: u32
}
let definition = StateMachineBuilder::<State, Event, Context>::new()
.initial_state(State::Idle)
.add_simple_state(State::Idle)
.add_simple_state(State::Working)
.add_transition(
State::Idle,
State::Working,
Event::Start,
None,
vec![Box::new(|ctx| {
ctx.work_count += 1;
println!("Started work #{}", ctx.work_count);
Ok(())
})],
)
.add_transition(
State::Working,
State::Idle,
Event::Finish,
None,
vec![Box::new(|ctx| {
println!("Finished work #{}", ctx.work_count);
Ok(())
})],
)
.build()?;
let context = Context { work_count: 0 };
let mut machine = StateMachine::new(definition, context)?;
machine.process_event(Event::new(Event::Start))?;
machine.process_event(Event::new(Event::Finish))?;use statechart::prelude::*;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum State {
Stopped,
Running,
RunningIdle,
RunningActive,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum Event { Start, Stop, Activate, Deactivate }
// Create composite state
let running_state = StateBuilder::<State, Event, ()>::new(State::Running)
.composite()
.initial(State::RunningIdle)
.build();
let definition = StateMachineBuilder::<State, Event, ()>::new()
.initial_state(State::Stopped)
.add_simple_state(State::Stopped)
.add_state(running_state)
.add_simple_state(State::RunningIdle)
.add_simple_state(State::RunningActive)
.add_transition(State::Stopped, State::Running, Event::Start, None, vec![])
.add_transition(State::Running, State::Stopped, Event::Stop, None, vec![])
.add_transition(State::RunningIdle, State::RunningActive, Event::Activate, None, vec![])
.add_transition(State::RunningActive, State::RunningIdle, Event::Deactivate, None, vec![])
.build()?;This API reference provides comprehensive coverage of the statechart library's functionality. For more examples and tutorials, see the User Guide.