Skip to content

Latest commit

 

History

History
468 lines (357 loc) · 11.9 KB

File metadata and controls

468 lines (357 loc) · 11.9 KB

Statechart API Reference

This document provides comprehensive API documentation for the Rust statechart library.

Table of Contents

  1. Core Concepts
  2. Builder API
  3. Model Types
  4. Runtime API
  5. Validation API
  6. Visualization API
  7. Error Handling
  8. Examples

Core Concepts

State Machine Definition

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

Key Traits

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,
}

Builder API

StateMachineBuilder

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()?;

Methods

  • new() -> Self - Create a new builder
  • initial_state(state: S) -> Self - Set the initial state
  • add_simple_state(state: S) -> Self - Add a simple state
  • add_state(state: State<S, C>) -> Self - Add a complex state
  • add_transition(source: S, target: S, event: E, guard: Option<Box<dyn Guard<C>>>, actions: Vec<Box<dyn Action<C>>>) -> Self - Add a transition
  • build() -> Result<StateMachineDefinition<S, E, C>, ValidationError> - Build and validate the definition

StateBuilder

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();

Methods

  • new(id: S) -> Self - Create a new state builder
  • simple() -> Self - Make this a simple state (default)
  • composite() -> Self - Make this a composite state
  • parallel() -> Self - Make this a parallel state
  • final_state() -> Self - Make this a final state
  • initial(state: S) -> Self - Set initial substate for composite states
  • on_entry(actions: Vec<Box<dyn Action<C>>>) -> Self - Add entry actions
  • on_exit(actions: Vec<Box<dyn Action<C>>>) -> Self - Add exit actions
  • build() -> State<S, C> - Build the state

Model Types

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);

Event

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);
}

Transition

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(())
}));

Guards

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);

Actions

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(())
    })
);

Runtime API

StateMachine

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;

Methods

  • new(definition: StateMachineDefinition<S, E, C>, context: C) -> Result<Self, StateMachineError> - Create new machine
  • process_event(&mut self, event: Event<E>) -> Result<bool, StateMachineError> - Process an event
  • queue_event(&mut self, event: Event<E>) - Queue an event for later processing
  • process_queued_events(&mut self) -> Result<Vec<bool>, StateMachineError> - Process all queued events
  • current_state(&self) -> &S - Get current state
  • is_in_state(&self, state: &S) -> bool - Check if in specific state
  • is_final(&self) -> bool - Check if in final state
  • context(&self) -> &C - Get context reference
  • context_mut(&mut self) -> &mut C - Get mutable context reference

Validation API

Validator

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);

Reachability Analysis

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);

Visualization API

DOT Export

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);

Error Handling

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);
    }
}

Error Types

  • StateMachineError - Runtime errors
  • ValidationError - Definition validation errors
  • ActionError - Action execution errors
  • BuilderError - Builder configuration errors

Examples

Simple State Machine

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);

State Machine with Context and Actions

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))?;

Hierarchical State Machine

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.