fsmy 0.3.0

A finite state machine library
docs.rs failed to build fsmy-0.3.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

FSMY

fsmy is a finite state machine rust library.

It is originally inspired by rust-fsm, with a few important differences, the main one being integrated support to contexts.

Features

  • On-transition context mutation - when leaving a state, entering one and along the transition edge
  • Custom alphabets for input, output, state and context
  • Parent states (regions)
  • DSL via fsmy-dsl
  • Mermaid.js diagrams
  • Final states detection
  • Virtual states
  • Ability to rewind states and context (see JournaledStateMachine)

Planned features

  • Function-based, non-static outputs
  • Parallel states
  • Other languages bindings

Usage

Add to your project

cargo add fsmy

Use via DSL

The DSL is similar to the one in rust-fsm-dsl but also accepts mutation closures to run on state transitions.


use fsmy::dsl::state_machine;

/// Context is optional, if not provided defaults to the type `()`
#[derive(Default, Debug)]
pub struct Context {
    counter: u8,
}

impl Context {
    pub fn counter(&self) -> u8 {
        self.counter
    }
}

state_machine! {
    #[repr(C)]
    #[derive(Debug)]
    #[alphabet(context(super::Context))]
    pub state_machine::Machine(Uninitialized)

    // Transitions can define outputs using square brackets after target state
    Uninitialized(Init) => Initialized[Running],

    // States can transition to themselves and also define a mutation to be run when the transition happens
    Initialized(Tick) => Initialized |context| { context.counter += 1; }
}

use state_machine::*;

let mut machine = Machine::default().start().unwrap();
let next = machine.consume(Input::Init);
assert!(next.is_ok());
assert_eq!(next.unwrap(), Some(&Output::Running));
assert_eq!(machine.context().counter(), 0);

let next = machine.consume(Input::Tick);
assert!(next.is_ok());
assert!(next.unwrap().is_none());
assert_eq!(machine.state(), &State::Initialized);
assert_eq!(machine.context().counter(), 1);

Context mutations

There are three different points where a context can be mutated, and all of them happen on a state transition and in the following order.

  • The on_leave mutation callback defined when defining a state via define_state, taken from the source state on the transition edge
  • The mutation defined when using add_transition_with_mutation (can also be done via DSL, see example above)
  • The on_enter mutation callback defined when defining a state via define_state, taken from the target state on the transition edge

In other words, when transitioning from state A to state B:

  • on_leave from state A is called
  • Mutation on the edge is called
  • on_enter on state B is called

Currently on_enter and on_leave are not supported by the DSL and can only be registered through the regular state machine rust api.

Custom alphabet and trait bounds

When defining a custom alphabet it is important that especially state implements some traits needed by the state machine implementations. You can use whatever type you want but if you use enums, which is very common, you can just

#[derive(Primitive)]
pub enum MyState {
    Init,
    Running,
    Ended
}

to automatically implement all of these traits for you.

Parent states

You can define parent states for certain states. Parent states are automatically active if one of their children is active, and therefore executes their on_enter and on_leave callbacks when appropriate.

This is supported partially by the DSL, allowing you to define parent states but no callbacks yet.

A parent-child relation can be defined like this using the DSL (when defining transitions):

...
Unloaded(LoadSong) => Loaded,
Loaded(Play) => Loaded.Playing,
Loaded.Playing(Pause) => Loaded.Paused,
Loaded.Stop => Unloaded
...

Virtual states

Cargo features

  • events: enables events bus
  • dsl: enables DSL macros (see below)
  • history: enables the journaled version of the state machine implementation