fsmy 0.1.0

A finite state machine library
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
use std::{
    fmt::{Debug, Display},
    hash::{Hash, Hasher},
    marker::PhantomData,
};

use derive_builder::Builder;
use derive_getters::Getters;
use thiserror::Error;

pub type Mutation<'a, C> = fn(&mut C);
pub type BoxedMutation<'a, C> = Box<Mutation<'a, C>>;

pub trait StateLike: Clone + Hash + PartialEq + Eq {}

impl<T> StateLike for T where T: Clone + Hash + PartialEq + Eq {}

/// Implementors of this trait are able to transform
pub trait ToPrimitive
where
    Self::Output: Copy + Hash + PartialEq + Eq + Debug,
{
    type Output;
    fn to_primitive(&self) -> Self::Output;
}

pub trait FromPrimitive: Sized + ToPrimitive {
    fn from_primitive(primitive: Self::Output) -> Option<Self>;
}

macro_rules! impl_trivial_primitives {
    ($($t:ty),*) => {
        $(
            impl ToPrimitive for $t {
                type Output = Self;
                fn to_primitive(&self) -> Self::Output {
                    *self
                }
            }

            impl FromPrimitive for $t {
                fn from_primitive(primitive: Self::Output) -> Option<Self> {
                    Some(primitive)
                }
            }
        )*
    };
}

impl_trivial_primitives! { u8, u16, u32, u64, i8, i16, i32, i64, isize, usize }

/// Errors that can be emitted from various points in the state machine
#[derive(Error, Debug)]
pub enum Error<'a, S, I> {
    /// Returned when trying to start a machine that has no initial state defined
    NoInitialState,
    /// Returned when consuming an input would lead to a non valid transition
    TransitionImpossible((&'a S, I)),
    /// Returned when trying to access a missing history entry for any reason
    NoHistory,
    /// Returned when trying to access a state that is unknown to the state machine
    UnknownState,
}

impl<S: Display, I: Display> Display for Error<'_, S, I> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Error::TransitionImpossible((from, event)) => {
                write!(f, "Cannot transition from {} with event {}", from, event)
            }
            Error::NoHistory => write!(f, "History entry is missing"),
            Error::UnknownState => write!(
                f,
                "A state is used without being defined in the state machine"
            ),
            Error::NoInitialState => write!(f, "Machine has no initial state defined"),
        }
    }
}

pub trait ConfigurableStateMachine<'a>
where
    Self::StateMachine: RunningStateMachine<'a>,
{
    type Input;
    type State;
    type Context;
    type Output;
    type StateMachine;

    /// Freezes the machine configuration and starts it - namely, it can
    /// consume events and evolve its state
    fn start(self) -> Result<Self::StateMachine, Error<'a, Self::State, Self::Input>>;

    /// Sets the initial state that will be used when the machine
    /// starts
    fn set_initial_state(&mut self, initial_state: Self::State);

    /// Defines a state known by this machine. Defining states can be done
    /// this way, which allows to specify `on_enter` and `on_leave` mutation callbacks,
    /// or it is implicitly done by adding transitions.
    ///
    /// If `state` was already defined it will update its definition and return `Some(old_definition)`.
    /// Otherwise it will define it and return `None`
    ///
    /// See `add_transition` and `add_transition_with_mutation` as well.
    fn define_state(
        &mut self,
        state: Self::State,
        options: state::Options<'a, Self>,
    ) -> Option<Self::State>;

    /// Defines a state known by this machine, along with a parent state relation. Defining states can be done
    /// this way, which allows to specify `on_enter` and `on_leave` mutation callbacks,
    /// or it is implicitly done by adding transitions.
    ///
    /// If `state` was already defined it will update its definition and return `Some(old_definition)`.
    /// Otherwise it will define it and return `None`
    ///
    /// See `add_transition` and `add_transition_with_mutation` as well.
    fn define_state_with_parent(
        &mut self,
        state: Self::State,
        parent: Self::State,
    ) -> Option<Self::State> {
        self.define_state(
            state,
            state::OptionsBuilder::default()
                .parent(Some(parent))
                .build()
                .unwrap(),
        )
    }

    /// Registers a valid state transition, along with an optional mutation. Any state involved in
    /// the transition is registered as a known state with no callback options.
    ///
    /// If a mutation is registered, the context is mutated right before the actual transition occurs, so if you try
    /// to access the current state or history it will still reflect the old state when the
    /// mutation closure is called.
    fn add_transition(
        &mut self,
        source_state: Self::State,
        event: Self::Input,
        target_state: Self::State,
        output: Option<Self::Output>,
        mutation: Option<fn(&mut Self::Context)>,
    );

    /// Like `add_transition` registers a valid transition, but it also accepts a function that mutates the current context.
    ///
    /// The context is mutated right before the actual transition occurs, so if you try
    /// to access the current state or history it will still reflect the old state when the
    /// mutation closure is called.
    fn add_transition_with_mutation(
        &mut self,
        source_state: Self::State,
        event: Self::Input,
        target_state: Self::State,
        output: Option<Self::Output>,
        mutation: fn(&mut Self::Context),
    ) {
        self.add_transition(source_state, event, target_state, output, Some(mutation));
    }

    /// Add a transition that doesn't mutate the context
    fn add_transition_immutable(
        &mut self,
        source_state: Self::State,
        event: Self::Input,
        target_state: Self::State,
        output: Option<Self::Output>,
    ) {
        self.add_transition(source_state, event, target_state, output, None);
    }
}

pub mod state {
    use super::*;

    #[derive(Builder, Getters, Debug, Clone)]
    #[builder(pattern = "owned")]
    pub struct Options<'a, T: ?Sized + ConfigurableStateMachine<'a>> {
        #[builder(default = false)]
        pub(crate) is_virtual: bool,
        #[builder(default = None)]
        pub(crate) parent: Option<T::State>,
        #[builder(default = None)]
        pub(crate) on_enter: Option<Mutation<'a, T::Context>>,
        #[builder(default = None)]
        pub(crate) on_leave: Option<Mutation<'a, T::Context>>,
    }
}

/// Generic state machine definition
pub trait RunningStateMachine<'a> {
    type Input;
    type State;
    type Context;
    type Output;

    /// Returns a view over the states defined in this machine
    fn states(&self) -> Vec<&Self::State>;

    /// The current state of the machine.
    fn state(&self) -> &Self::State;

    /// The current context of the machine.
    fn context(&self) -> &Self::Context;

    /// Whether the state machine is in a final state, meaning there is no
    /// input it can consume that would lead it in some other state.
    fn is_final(&self) -> bool;

    /// Returns `true` if a given state is the currently active one.
    /// All the parent states of a given one are implicitly active if one
    /// of their children is active.
    fn is_active(&self, state: Self::State) -> bool;

    /// Consumes an input. If consuming this input leads the machine to a
    /// new state - the transition is valid - an `Ok` is returned containing
    /// the output of the transition if one was defined and `None` if the transition
    /// has no output.
    ///
    /// If the transition is invalid, an `Err` is returned instead.
    #[allow(clippy::type_complexity)]
    fn consume(
        &mut self,
        event: Self::Input,
    ) -> Result<Option<&Self::Output>, Error<'_, Self::State, Self::Input>>;
}

/// A trait implemented by state machines that can give direct access
/// to their internal state in a mutable fashion. See for example [`JournaledStateMachine`].
#[allow(dead_code)]
pub(crate) trait StateMachineMut<'a>: RunningStateMachine<'a> {
    /// Sets the state in the machine. This should never be called
    /// by user code.
    fn set_state(&mut self, value: Self::State);

    /// A mutable reference to the state machine context.
    fn context_mut(&mut self) -> &mut Self::Context;
}

/// Notable events that can be emitted from the machine.
///
/// Note that events are emitted only when `events` feature is activated.
#[derive(Debug, Clone)]
pub enum StateMachineEvent<S> {
    StateChanged((S, S)),
}

#[cfg(feature = "events")]
pub trait EventProducer<S> {
    fn emit(
        &self,
        event: StateMachineEvent<S>,
    ) -> Result<(), crossbeam_channel::SendError<StateMachineEvent<S>>>;
}

/// A state machine that can also remember its transition history
pub trait History<'a>: RunningStateMachine<'a> {
    type Len;

    /// The length of the history, namely how many states transition have occured
    fn history_len(&self) -> Self::Len;

    /// Rewinds the state machine to its previous state. The last state is popped
    /// from the history stack and set as the current one.
    fn rewind(&mut self) -> Result<(), Error<'_, Self::State, Self::Input>>;
}

pub trait StateDefinition<'a, S, C> {
    fn value(&self) -> &S;
    fn on_enter(&self) -> Option<&Mutation<'a, C>>;
    fn on_leave(&self) -> Option<&Mutation<'a, C>>;
}

#[derive(Getters, Builder)]
pub struct StateDefWrapper<'a, S, C> {
    #[getter(skip)]
    marker: PhantomData<&'a mut C>,
    value: S,
    on_enter: Option<Mutation<'a, C>>,
    on_leave: Option<Mutation<'a, C>>,
    is_virtual: bool,
}

impl<'a, S, C> StateDefinition<'a, S, C> for StateDefWrapper<'a, S, C> {
    fn value(&self) -> &S {
        &self.value
    }

    fn on_enter(&self) -> Option<&Mutation<'a, C>> {
        self.on_enter.as_ref()
    }

    fn on_leave(&self) -> Option<&Mutation<'a, C>> {
        self.on_leave.as_ref()
    }
}

impl<S, C> Debug for StateDefWrapper<'_, S, C>
where
    S: Debug,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("StateWrapper")
            .field("marker", &self.marker)
            .field("value", &self.value)
            .field("on_enter", &self.on_enter.as_ref().map(|_| "{function}"))
            .field("on_leave", &self.on_leave.as_ref().map(|_| "{function}"))
            .finish()
    }
}

impl<S, C> Clone for StateDefWrapper<'_, S, C>
where
    S: Clone,
{
    fn clone(&self) -> Self {
        // This needs to be manually implemented because the derive
        // macro would impose C: Clone, which is not needed here
        Self {
            marker: self.marker,
            value: self.value.clone(),
            on_enter: self.on_enter,
            on_leave: self.on_leave,
            is_virtual: false,
        }
    }
}

impl<S, C> Hash for StateDefWrapper<'_, S, C>
where
    S: Hash,
{
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.value.hash(state);
    }
}

impl<S, C> PartialEq for StateDefWrapper<'_, S, C>
where
    S: PartialEq,
{
    fn eq(&self, other: &Self) -> bool {
        self.value == other.value
    }
}

impl<S, C> Eq for StateDefWrapper<'_, S, C> where S: PartialEq {}

impl<'a, S, C> StateDefWrapper<'a, S, C> {
    pub fn new(value: S) -> Self {
        Self {
            marker: PhantomData,
            value,
            on_enter: None,
            on_leave: None,
            is_virtual: false,
        }
    }

    pub fn with_mutations(
        value: S,
        on_enter: Option<Mutation<'a, C>>,
        on_leave: Option<Mutation<'a, C>>,
    ) -> Self {
        Self {
            marker: PhantomData,
            value,
            on_enter,
            on_leave,
            is_virtual: false,
        }
    }
}

impl<S, C> AsRef<S> for StateDefWrapper<'_, S, C> {
    fn as_ref(&self) -> &S {
        self.value()
    }
}

impl<S, C> ToPrimitive for StateDefWrapper<'_, S, C>
where
    S: ToPrimitive,
{
    type Output = <S as ToPrimitive>::Output;

    fn to_primitive(&self) -> Self::Output {
        self.value().to_primitive()
    }
}

impl<S, C> FromPrimitive for StateDefWrapper<'_, S, C>
where
    S: FromPrimitive,
{
    fn from_primitive(primitive: Self::Output) -> Option<Self> {
        S::from_primitive(primitive).map(StateDefWrapper::new)
    }
}