fsmy 0.2.0

A finite state machine library
# 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

```sh
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.

```rust ignore

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

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

to automatically implement all of these traits for you.

## `Cargo` features

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