statement 0.1.4

An event-driven state machine library for Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
//! This library provides an Event-Driven State Machine implementation for Rust
//!
//! A State Machine describes a struct that maintains a *State* variable in a predictable way,
//! using a set of *Transitions* to describe how the state may change. Transitions are triggered
//! by *Events*, which are generated by the environment, and may in turn cause *Effects* to execute,
//! which can influence the environment. State Machines may also carry arbitrary *Data*, which can
//! be mutated by Effects.
//!
//! In this State Machine implementation, the State Machine is operated by providing it with
//! Events through the [StateMachine::handle_event] method. Events can be anything, but it is common
//! to represent them with an Enum.
//!
//! States themselves are very restricted to allow for Any / AllOf matching, and typically should
//! also be implemented with an enum that derives Copy, Clone, Eq, PartialEq, and Debug.
//!
//! # Defining Transitions
//!
//! State Machines in statement are simply a thin wrapper over a state object and a list of
//! transitions. To define a state machine, you need to:
//! 1. Create a [StateMachineFactory] using [StateMachineFactory::new]
//! 2. Add transitions using one or more of:
//!     - [StateMachineFactory::with_predicated_transition]
//!     - [StateMachineFactory::with_predicated_transition_effect]
//!     - [StateMachineFactory::with_event_transition]
//!     - [StateMachineFactory::with_event_transition_effect]
//!     - [StateMachineFactory::with_auto_transition]
//!     - [StateMachineFactory::with_custom_transition]
//! 3. Lock your factory into a [LockedStateMachineFactory] by calling [StateMachineFactory::lock]
//! 4. Create a state machine by calling [LockedStateMachineFactory::build]
//!
//! # Transitions
//!
//! Transitions (represented by the [StateMachineTransition] struct) must specify the State or set
//! of initial states (as a [FromState]) that may trigger them:
//! - [FromState::Any]: Any starting state - this Transition will be evaluated for all events.
//! - [FromState::AnyOf]: Any starting state in the provided list.
//! - [FromState::From]: The specific provided started state. FromState implements [From] for this
//! variant, so the variant can be elided for the common case.
//!
//! Transitions may also optionally provide a predicate to apply custom logic to decide whether the
//! Transition is applied. Transitions may also be triggered from any ([FromState::Any]) state,
//! meaning that they are considered for any Event.
//!
//! Transitions must also describe the state that they transition the State Machine into. The to_state
//! of a transition can be represented as one of the following:
//! - [To]: A specific, pre-defined state. ToState implements [From] for this variant, so the variant
//! can be elided for the common case.
//! - [Same]: Whatever state the transition started from; this makes the transition a no-op for the
//! state machine, but side effects may still be executed. This is useful in some cases, such as in
//! transition loggers.
//! - [Calc]: Allows for dynamic target state calculation, when a given transition may result in
//! more than one target states. This is something of an antipattern; these should preferentially
//! be represented as multiple transitions with different predicates.
//!
//! # Event Lifecycle
//!
//! 1. Handle event called.
//! 2. For each defined transition:
//!
//!     2a. Determine if the from_state of the transition matches the current state.
//!         If false, break and move on to the next transition.
//!
//!     2b. Determine the to_state of the transition.
//!
//!     2c. Run the transition's predicate, if any.
//!         If false (or no predicate), break and move on to the next transition.
//!
//!     2d. Run the transition's effect, if any.
//!
//!     2e. Transition the state machine to the to_state determined in 2b above.
//!
//! 3. If the State Machine has cycle set to true, return to 2.
//!
#![deny(missing_docs)]

use std::fmt::{Debug};
use std::ops::Deref;
use std::sync::Arc;
use thiserror::Error;
use crate::ToState::{Calc, Same, To};

/// State Machine instance, usually created by calling create on a [LockedStateMachineFactory]
#[derive(Default, Clone)]
pub struct StateMachine<'a, TEvent, TState: PartialEq<TState> + Clone + Send + 'a, TData, TErr = Box<dyn std::error::Error>>
{
    /// The current state of the `StateMachine`
    pub state: TState,
    /// All of the transitions that are valid for this state machine. Note that this list may be
    /// shared with other state machine instances.
    pub transitions: Arc<Vec<StateMachineTransition<'a, TEvent, TState, TData, TErr>>>,
    /// Data associated with this state machine instance. This may be used to track information that
    /// cannot be expressed conveniently in Events, or it may be data which Side Effects act on. In
    /// the latter case, `TData` may need to implement interior mutability.
    pub data: TData,
    /// True if this state machine automatically re-runs evaluation after a transition, potentially
    /// executing multiple state transitions for one event.
    pub cycle: bool,
}

impl <'a, TEvent, TState: PartialEq<TState> + Clone + Send + Eq + PartialEq + 'a, TData, TErr> StateMachine<'a, TEvent, TState, TData, TErr>
{
    fn new(cycle: bool, initial_state: TState, initial_data: TData) -> Self {
        Self {
            cycle,
            state: initial_state,
            data: initial_data,
            transitions: Arc::new(Vec::new()),
        }
    }

    /// Creates a `StateMachine` from a pre-existing set of transitions.
    pub fn with_transitions(mut self, transitions: Arc<Vec<StateMachineTransition<'a, TEvent, TState, TData, TErr>>>) -> Self {
        self.transitions = transitions.clone();
        self
    }

    /// Handles an Event, causing the state machine to execute one or more Transitions.
    pub fn handle_event(&mut self, event: TEvent) -> Result<&TState, StateMachineError<TState, TErr>> {
        loop {
            let mut transition_occurred = false;
            for transition in self.transitions.deref() {

                // Determine if the current state matches the from_state of the transition
                let from_state_matches = match &transition.from_state {
                    FromState::Any => true,
                    FromState::AnyOf(states) => states.iter().any(|s|s == &self.state),
                    FromState::From(state) => state == &self.state
                };

                // If the from_state matches, we need to consider whether this transition should execute
                if from_state_matches {

                    // Determine the result state and whether we need to proceed after this transition
                    // If proceed is true OR this transition changes the state, we will continue to
                    // evaluate further transitions after executing this one.
                    let to_state = match &transition.get_to_state {
                        To(to_state) => to_state.clone(),
                        Calc(get_to_state) => {
                            let data = StateTransitionToStateData {
                                data: &mut self.data,
                                event: &event,
                                from: &self.state,
                            };
                            get_to_state.deref()(data)
                        },
                        Same => self.state.clone()
                    };

                    // This sets up a data item to pass to the Predicate method (if any) and the
                    // Effect method (if any)
                    let transition_effect_data = StateTransitionEffectData {
                        name: &transition.name,
                        data: &mut self.data,
                        event: &event,
                        from: &self.state,
                        to: &to_state
                    };

                    // If there is a Predicate on this Transition, execute it and if it returns
                    // false, skip to the next Transition
                    if let Some(predicate) = &transition.event_predicate {
                        if !predicate(&transition_effect_data) {
                            continue;
                        }
                    }

                    // If there is an Effect on this Transition, execute it
                    if let Some(effect) = &transition.effect {
                        effect(transition_effect_data)
                            .map_err(|e| StateMachineError::EffectError(self.state.clone(), to_state.clone(), e))?;
                    }

                    // If proceed is false or we changed state, mark transition_occurred as true so
                    // that we evaluate all of the transitions again.
                    if &self.state != &to_state {
                        self.state = to_state;
                        transition_occurred = true;
                    }
                }
            }

            // If no transition occurred, we can end evaluation
            if !self.cycle || !transition_occurred {
                break;
            }
        }
        Ok(&self.state)
    }
}

/// Locked Factory for StateMachines. This struct is created by calling .lock() on a
/// StateMachineFactory, usually after defining all transitions needed.
pub struct LockedStateMachineFactory<'a, TEvent, TState: PartialEq<TState> + Clone + Send + 'a, TData = (), TErr = Box<dyn std::error::Error>> {
    transitions: Arc<Vec<StateMachineTransition<'a, TEvent, TState, TData, TErr>>>,
    cycle: bool,
}

impl <'a, TEvent, TState: PartialEq<TState> + Clone + Send + Eq + PartialEq + 'a, TData, TErr> LockedStateMachineFactory<'a, TEvent, TState, TData, TErr> {
    /// Builds a StateMachine with a specified initial state and initial data.
    pub fn build(&self, initial_state: TState, initial_data: TData) -> StateMachine<'a, TEvent, TState, TData, TErr> {
        StateMachine::new(self.cycle, initial_state, initial_data).with_transitions(self.transitions.clone())
    }
}

/// Factory for StateMachines. This struct can be used to define a series of Transitions that
/// may be subsequently used to create multiple state machine instances with those same
/// transitions.
#[derive(Default)]
pub struct StateMachineFactory<'a, TEvent, TState: PartialEq<TState> + Clone + Send + 'a, TData, TErr = Box<dyn std::error::Error>> {
    cycle: bool,
    transitions: Vec<StateMachineTransition<'a, TEvent, TState, TData, TErr>>,
}

impl <'a, TEvent, TState: PartialEq<TState> + Clone + Send + Eq + PartialEq + 'a, TData, TErr> StateMachineFactory<'a, TEvent, TState, TData, TErr> {
    /// Creates a new `StateMachineFactory`
    pub fn new() -> Self {
        Self {
            cycle: false,
            transitions: Vec::new(),
        }
    }

    /// Controls whether a state machine loops back after a transition.
    pub fn cycle(self, cycle: bool) -> Self {
        Self {
            cycle,
            transitions: self.transitions
        }
    }

    /// Creates a LockedStateMachineFactory which can be used to build StateMachine instances
    /// with the Transitions defined in this StateMachineFactory.
    pub fn lock(self) -> LockedStateMachineFactory<'a, TEvent, TState, TData, TErr> {
        LockedStateMachineFactory {
            cycle: self.cycle,
            transitions: Arc::new(self.transitions)
        }
    }

    /// Adds an externally-created transition to this `StateMachineFactory`
    pub fn with_custom_transition(mut self, transition: StateMachineTransition<'a, TEvent, TState, TData, TErr>) -> Self
    {
        self.transitions.push(transition);
        self
    }

    /// Adds a named Transition to the State Machine definition with no predicate and no side
    /// effects. If this State Machine has cycle enabled, this transition will execute
    /// automatically, essentially skipping the From state. If Cycle is not enabled, the State
    /// Machine will transition to the To state with any future event.
    pub fn with_named_auto_transition(mut self, name: impl Into<String>, from_state: impl Into<FromState<TState>>, get_to_state: impl Into<ToState<TEvent, TState, TData>>) -> Self
    {
        self.transitions.push(StateMachineTransition::new(Some(name.into()), None, from_state.into(), get_to_state.into(), None));
        self
    }

    /// Adds a named Transition to the State Machine definition with a side effect and no predicate.
    /// If this State Machine has cycle enabled, this transition will execute automatically,
    /// essentially skipping the From state after executing the side effect. If Cycle is not
    /// enabled, the State Machine will transition to the To state with any future event.
    pub fn with_named_transition_effect(mut self, name: impl Into<String>, from_state: impl Into<FromState<TState>>, get_to_state: impl Into<ToState<TEvent, TState, TData>>, effect: impl Fn(StateTransitionEffectData<TEvent, TState, TData>) -> Result<(), TErr> + Send + 'a) -> Self
    {
        self.transitions.push(StateMachineTransition::new(Some(name.into()), None, from_state.into(), get_to_state.into(), Some(Box::new(effect))));
        self
    }

    /// Adds a name Transition to the State Machine definition with a predicate and no Side Effect.
    /// This transition will test the predicate for any event and move to the To state if the
    /// Predicate returns true.
    pub fn with_named_predicated_transition(mut self, name: impl Into<String>, from_state: impl Into<FromState<TState>>, get_to_state: impl Into<ToState<TEvent, TState, TData>>, event_predicate: impl Fn(&StateTransitionEffectData<TEvent, TState, TData>) -> bool + Send + 'a) -> Self
    {
        self.transitions.push(StateMachineTransition::new(Some(name.into()), Some(Box::new(event_predicate)), from_state.into(), get_to_state.into(), None));
        self
    }

    /// Adds a named Transition to the State Machine definition with a predicate and a Side Effect.
    /// This transition will test the predicate for any event and execute the Side Effect then move
    /// to the To state if the Predicate returns true.
    pub fn with_named_predicated_transition_effect(mut self, name: impl Into<String>, from_state: impl Into<FromState<TState>>, get_to_state: impl Into<ToState<TEvent, TState, TData>>, event_predicate: impl Fn(&StateTransitionEffectData<TEvent, TState, TData>) -> bool + Send + 'a, effect: impl Fn(StateTransitionEffectData<TEvent, TState, TData>) -> Result<(), TErr> + Send + 'a) -> Self
    {
        self.transitions.push(StateMachineTransition::new(Some(name.into()), Some(Box::new(event_predicate)), from_state.into(), get_to_state.into(), Some(Box::new(effect))));
        self
    }

    /// Adds an unnamed Transition to the State Machine definition with no predicate and no side
    /// effects. If this State Machine has cycle enabled, this transition will execute
    /// automatically, essentially skipping the From state. If Cycle is not enabled, the State
    /// Machine will transition to the To state with any future event.
    pub fn with_auto_transition(mut self, from_state: impl Into<FromState<TState>>, get_to_state: impl Into<ToState<TEvent, TState, TData>>) -> Self
    {
        self.transitions.push(StateMachineTransition::new(None, None, from_state.into(), get_to_state.into(), None));
        self
    }

    /// Adds an unnamed Transition to the State Machine definition with a side effect and no
    /// predicate. If this State Machine has cycle enabled, this transition will execute
    /// automatically, essentially skipping the From state after executing the side effect. If
    /// Cycle is not enabled, the State Machine will transition to the To state with any future
    /// event.
    pub fn with_transition_effect(mut self, from_state: impl Into<FromState<TState>>, get_to_state: impl Into<ToState<TEvent, TState, TData>>, effect: impl Fn(StateTransitionEffectData<TEvent, TState, TData>) -> Result<(), TErr> + Send + 'a) -> Self
    {
        self.transitions.push(StateMachineTransition::new(None, None, from_state.into(), get_to_state.into(), Some(Box::new(effect))));
        self
    }

    /// Adds an unnamed Transition to the State Machine definition with a predicate and no Side
    /// Effect. This transition will test the predicate for any event and move to the To state if
    /// the Predicate returns true.
    pub fn with_predicated_transition(mut self, from_state: impl Into<FromState<TState>>, get_to_state: impl Into<ToState<TEvent, TState, TData>>, event_predicate: impl Fn(&StateTransitionEffectData<TEvent, TState, TData>) -> bool + Send + 'a) -> Self
    {
        self.transitions.push(StateMachineTransition::new(None, Some(Box::new(event_predicate)), from_state.into(), get_to_state.into(), None));
        self
    }

    /// Adds an unnamed Transition to the State Machine definition with a predicate and a Side
    /// Effect. This transition will test the predicate for any event and execute the Side Effect
    /// then move to the To state if the Predicate returns true.
    pub fn with_predicated_transition_effect(mut self, from_state: impl Into<FromState<TState>>, get_to_state: impl Into<ToState<TEvent, TState, TData>>, event_predicate: impl Fn(&StateTransitionEffectData<TEvent, TState, TData>) -> bool + Send + 'a, effect: impl Fn(StateTransitionEffectData<TEvent, TState, TData>) -> Result<(), TErr> + Send + 'a) -> Self
    {
        self.transitions.push(StateMachineTransition::new(None, Some(Box::new(event_predicate)), from_state.into(), get_to_state.into(), Some(Box::new(effect))));
        self
    }
}

impl <'a, TEvent, TState: PartialEq<TState> + Clone + Send + Eq + PartialEq + 'a, TData, TErr> StateMachineFactory<'a, TEvent, TState, TData, TErr>
where TEvent: PartialEq<TEvent> + Sync
{
    /// Adds a named Transition to the State Machine definition whose predicate checks for equality with a
    /// provided Event reference. This is syntactic sugar for `.with_predicated_transition(..)` with
    /// an equality Predicate.
    pub fn with_named_event_transition<'b>(mut self, name: impl Into<String>, event: &'a TEvent, from_state: impl Into<FromState<TState>>, get_to_state: impl Into<ToState<TEvent, TState, TData>>) -> Self
    {
        self.transitions.push(
            StateMachineTransition::new(
                Some(name.into()),
                Some(Box::new(|e| *event == *e.event)),
                from_state.into(),
                get_to_state.into(),
                None
            )
        );
        self
    }

    /// Adds a named Transition with a side effect to the State Machine definition whose predicate checks
    /// for equality with a provided Event reference. This is syntactic sugar for
    /// `.with_predicated_transition(..)` with an equality Predicate.
    pub fn with_named_event_transition_effect(mut self, name: impl Into<String>, event: &'a TEvent, from_state: impl Into<FromState<TState>>, get_to_state: impl Into<ToState<TEvent, TState, TData>>, effect: impl Fn(StateTransitionEffectData<TEvent, TState, TData>) -> Result<(), TErr> + Send + 'a) -> Self
    {
        self.transitions.push(
            StateMachineTransition::new(
                Some(name.into()),
                Some(Box::new(|e| *event == *e.event)),
                from_state.into(),
                get_to_state.into(),
                Some(Box::new(effect))
            )
        );
        self
    }
    /// Adds an unnamed Transition to the State Machine definition whose predicate checks for
    /// equality with a provided Event reference. This is syntactic sugar for
    /// `.with_predicated_transition(..)` with an equality Predicate.
    pub fn with_event_transition<'b>(mut self, event: &'a TEvent, from_state: impl Into<FromState<TState>>, get_to_state: impl Into<ToState<TEvent, TState, TData>>) -> Self
    {
        self.transitions.push(
            StateMachineTransition::new(
                None,
                Some(Box::new(|e| *event == *e.event)),
                from_state.into(),
                get_to_state.into(),
                None
            )
        );
        self
    }

    /// Adds an unnamed Transition with a side effect to the State Machine definition whose
    /// predicate checks for equality with a provided Event reference. This is syntactic sugar for
    /// `.with_predicated_transition(..)` with an equality Predicate.
    pub fn with_event_transition_effect(mut self, event: &'a TEvent, from_state: impl Into<FromState<TState>>, get_to_state: impl Into<ToState<TEvent, TState, TData>>, effect: impl Fn(StateTransitionEffectData<TEvent, TState, TData>) -> Result<(), TErr> + Send + 'a) -> Self
    {
        self.transitions.push(
            StateMachineTransition::new(
                None,
                Some(Box::new(|e| *event == *e.event)),
                from_state.into(),
                get_to_state.into(),
                Some(Box::new(effect))
            )
        );
        self
    }
}

/// Basic error type for [StateMachine]
#[derive(Error, Debug)]
pub enum StateMachineError<TState: Send + Clone + Eq + PartialEq, TErr = Box<dyn std::error::Error>> {
    /// Basic error type for [StateMachine::handle_event]
    #[error("error running effect moving from state {0:?} to {1:?}: {2:?}")]
    EffectError(TState, TState, TErr)
}

/// Describes a Transition between States, potentially with a Predicate and/or Effect
pub struct StateMachineTransition<'a, TEvent, TState: PartialEq<TState> + Clone + Send + 'a, TData, TErr = Box<dyn std::error::Error>>
{
    name: Option<String>,
    from_state: FromState<TState>,
    get_to_state: ToState<TEvent, TState, TData>,
    event_predicate: Option<Box<dyn Fn(&StateTransitionEffectData<TEvent, TState, TData>) -> bool + Send + 'a>>,
    effect: Option<Box<dyn Fn(StateTransitionEffectData<TEvent, TState, TData>) -> Result<(), TErr> + Send + 'a>>
}

impl <'a, TEvent, TState: PartialEq<TState> + Clone + Send + 'a, TData, TErr> StateMachineTransition<'a, TEvent, TState, TData, TErr> {
    fn new(
        name: Option<String>,
        event_predicate: Option<Box<dyn Fn(&StateTransitionEffectData<TEvent, TState, TData>) -> bool + Send + 'a>>,
        from_state: FromState<TState>,
        get_to_state: ToState<TEvent, TState, TData>,
        effect: Option<Box<dyn Fn(StateTransitionEffectData<TEvent, TState, TData>) -> Result<(), TErr> + Send + 'a>>,
    ) -> Self
    {
        Self {
            name,
            event_predicate,
            from_state,
            get_to_state,
            effect
        }
    }
}

/// Indicates the State or set of States from which a Transition is valid
#[derive(Clone, Eq, PartialEq)]
pub enum FromState<TState: PartialEq<TState> + Clone> {
    /// Indicates that a Transition is valid from any State
    Any,
    /// Indicates that a Transition is valid from any State in the provided Vector
    AnyOf(Vec<TState>),
    /// Indicates that a Transition is valid only from the specified State
    From(TState)
}

impl <TState: PartialEq<TState> + Clone> From<TState> for FromState<TState> {
    fn from(value: TState) -> Self {
        FromState::From(value)
    }
}

/// Indicates how a result State is determined after transitioning
pub enum ToState<TEvent, TState: PartialEq<TState> + Clone + Send, TData> {
    /// Indicates that a Transition should be applied without changing state.
    /// This is a special case, intended for Transitions that want to execute Effects
    /// without causing a state change (e.g. Loggers). Count As Transition should be
    /// set to true for Transitions that should stop execution, or false for Transactions
    /// that should not stop execution.
    Same,
    /// Specifies that a Transition will cause the State Machine to move to the specified State.
    To(TState),
    /// Allows a Transition to provide bespoke logic for determining which State to transition into.
    Calc(Box<dyn Fn(StateTransitionToStateData<TEvent, TState, TData>) -> TState>)
}

impl <TEvent, TState: PartialEq<TState> + Clone + Send, TData> From<TState> for ToState<TEvent, TState, TData> {
    fn from(value: TState) -> Self {
        ToState::<TEvent, TState, TData>::To(value)
    }
}

/// Data passed to a Transition Effect callback.
#[derive(Clone)]
pub struct StateTransitionEffectData<'a, TEvent, TState, TData> {
    /// The name of the transition, if any.
    pub name: &'a Option<String>,
    /// The event causing this transition to occur.
    pub event: &'a TEvent,
    /// The current data associated with the State Machine.
    pub data: &'a TData,
    /// The state that is being transitioned from.
    pub from: &'a TState,
    /// The state that is being transitioned into.
    pub to: &'a TState
}

/// Data passed to a Transition ToState callback.
#[derive(Clone)]
pub struct StateTransitionToStateData<'a, TEvent, TState, TData> {
    /// The event causing this transition to occur.
    pub event: &'a TEvent,
    /// The current data associated with the State Machine.
    pub data: &'a TData,
    /// The state that is being transitioned from.
    pub from: &'a TState,
}

#[cfg(test)]
mod unit_tests {
    use std::sync::atomic::{AtomicBool, Ordering};
    use anyhow::{anyhow};
    use thiserror::Error;
    use crate::{StateMachineFactory, StateMachineError};
    use crate::FromState::From;
    use crate::ToState::To;

    #[test]
    fn test_state_machine() {
        #[derive(Eq, PartialEq)]
        enum StateMachineMessage {
            GoToTwo,
            GoToThree
        }

        let go_to_two_happened = AtomicBool::new(false);
        let go_to_three_happened = AtomicBool::new(false);
        let mut sm = StateMachineFactory::<StateMachineMessage, u32, ()>::new()
            .with_event_transition_effect(
                &StateMachineMessage::GoToTwo,
                1,
                2,
                |_| {
                    go_to_two_happened.store(true, Ordering::SeqCst);
                    Ok(())
                }
            )
            .with_event_transition_effect(
                &StateMachineMessage::GoToThree,
                2,
                3,
                |_| {
                    go_to_three_happened.store(true, Ordering::SeqCst);
                    Ok(())
                }
            ).lock().build(1, ());

        // Assert our starting state is 1
        assert_eq!(1, sm.state);
        // Nothing is going to happen, because no transition is defined for GoToThree from the current state of 1
        assert_eq!(&1, sm.handle_event(StateMachineMessage::GoToThree).expect("unexpected error"));
        // This will transition the state to 2
        assert_eq!(&2, sm.handle_event(StateMachineMessage::GoToTwo).expect("unexpected error"));
        // Assert that the side effect occurred
        assert!(go_to_two_happened.load(Ordering::SeqCst), "effect from GoToTwo did not happen when expected");
        // Assert that we are in state 2
        assert_eq!(2, sm.state);

        // Nothing is going to happen, because no transition is defined for GoToTwo from the current state of 2
        assert_eq!(&2, sm.handle_event(StateMachineMessage::GoToTwo).expect("unexpected error"));
        // This will transition the state to 3
        assert_eq!(&3, sm.handle_event(StateMachineMessage::GoToThree).expect("unexpected error"));
        // Assert that the side effect occurred
        assert!(go_to_three_happened.load(Ordering::SeqCst), "effect from GoToThree did not happen when expected");
        // Assert that we are in state 3
        assert_eq!(3, sm.state);
    }

    #[test]
    fn test_double_transition<'a>() -> anyhow::Result<()> {
        #[derive(Eq, PartialEq)]
        enum StateMachineMessage {
            GoToTwo
        }

        // State here is just an integer
        let factory = StateMachineFactory::<StateMachineMessage, u32, ()>::new()
            // Evaluate all transitions in a loop
            // until no transition occurs
            .cycle(true)
            // When we receive a GoToTwo event
            // while in state 1, go to state 2
            .with_event_transition(
                &StateMachineMessage::GoToTwo,
                1,
                2
            )
            // When we transition to state 2,
            // immediately transition to state 3
            .with_auto_transition(
                2,
                3
            )
            // Lock the factory object so that
            // we can build a state machine
            .lock();

        // Build the state machine, with an empty () as data
        // (we don't care about data for this example)
        let mut sm = factory.build(1, ());

        // The StateMachine starts in state 1
        assert_eq!(1, sm.state);

        // Handling an event tells us what state we end up in
        match sm.handle_event(StateMachineMessage::GoToTwo) {
            Ok(state) => {
                assert_eq!(3, *state);
            }
            Err(StateMachineError::EffectError(from, to, e)) => {
                return Err(anyhow!("error changing state from {} to {}: {}", from, to, e));
            }
        };

        // Because of the two transitions that we defined,
        // we end up in state 3
        assert_eq!(3, sm.state);
        Ok(())
    }

    #[test]
    fn test_effect_error() -> anyhow::Result<()> {
        #[derive(Eq, PartialEq, Debug)]
        enum StateMachineMessage {
            GoToTwo
        }

        #[derive(Error, Debug, Eq, PartialEq)]
        enum TestError {
            #[error("test error")]
            TestError
        }

        let mut sm = StateMachineFactory::new()
            .with_event_transition_effect(
                &StateMachineMessage::GoToTwo,
                From(1),
                To(2),
                |_| {
                    Err(TestError::TestError)
                }
            ).lock().build(1, ());

        match sm.handle_event(StateMachineMessage::GoToTwo) {
            Ok(_) => {
                Err(anyhow!("expected an error"))
            },
            Err(StateMachineError::EffectError(from, to, cause)) => {
                assert_eq!(1, from);
                assert_eq!(2, to);
                assert_eq!(cause, TestError::TestError);
                Ok(())
            }
        }
    }
}