1pub mod error;
2
3use std::mem;
4
5use derive_where::derive_where;
6pub use rust_fsm;
7use spru::common::error::AnyResult;
8use tagset::tagset;
9use telety::telety;
10
11use crate::{Strictness, cloned};
12
13pub trait StateMachineTy: Clone + rust_fsm::StateMachineImpl + 'static {}
15
16impl<T: Clone + rust_fsm::StateMachineImpl + 'static> StateMachineTy for T {}
17
18#[derive_where(Debug, Clone, Serialize, Deserialize; FSM::State)]
21pub struct Fsm<FSM: StateMachineTy>(FSM::State);
22
23impl<FSM: StateMachineTy> Fsm<FSM> {
24 pub fn current(&self) -> &FSM::State {
25 &self.0
26 }
27}
28
29pub fn default<FSM: StateMachineTy>() -> Create<FSM> {
30 create(FSM::INITIAL_STATE)
31}
32
33pub fn create<FSM: StateMachineTy>(initial_state: FSM::State) -> Create<FSM> {
34 cloned::create(Fsm(initial_state))
35}
36
37pub fn transition<FSM: StateMachineTy>(input: FSM::Input) -> Transition<FSM> {
40 Transition {
41 input,
42 strictness: Strictness::AllOrError,
43 }
44}
45
46pub fn try_transition<FSM: StateMachineTy>(input: FSM::Input) -> Transition<FSM> {
48 Transition {
49 input,
50 strictness: Strictness::BestEffort,
51 }
52}
53
54pub fn set<FSM: StateMachineTy>(new_state: FSM::State) -> Set<FSM> {
56 Set { new_state }
57}
58
59pub fn destroy<FSM: StateMachineTy>() -> Destroy<FSM> {
60 cloned::destroy()
61}
62
63#[telety(crate::fsm)]
64#[tagset(Create<FSM>)]
65#[tagset(Set<FSM>)]
66#[tagset(Transition<FSM>)]
67#[tagset(Destroy<FSM>)]
68#[tagset(reserved(..8))]
69pub struct Actions<FSM: StateMachineTy>;
70
71#[derive(Debug, Clone, crate::FromInfallible, thiserror::Error)]
72#[error("FSM error: {0}")]
73pub enum Error {
74 Transition(#[from] error::Transition),
75}
76
77pub type Create<FSM> = cloned::Create<Fsm<FSM>>;
78
79pub type Destroy<FSM> = cloned::Destroy<Fsm<FSM>>;
80
81#[derive_where(Debug, Clone, Serialize, Deserialize; FSM::Input)]
82#[derive(spru::action::Update)]
83#[must_use]
84pub struct Transition<FSM: StateMachineTy> {
85 input: FSM::Input,
86 strictness: Strictness,
87}
88
89impl<FSM> spru::action::Update for Transition<FSM>
90where
91 FSM: StateMachineTy<State: Clone, Input: Clone>,
92{
93 type T = Fsm<FSM>;
94 type Undo = Set<FSM>;
95
96 #[allow(refining_impl_trait)]
97 fn update(&self, value: &mut Self::T) -> AnyResult<Option<Self::Undo>> {
98 match FSM::transition(&value.0, &self.input) {
99 Some(new_state) => {
100 let old_state = mem::replace(&mut value.0, new_state);
101 Ok(Some(Set {
102 new_state: old_state,
103 }))
104 }
105 None => match self.strictness {
106 Strictness::BestEffort => Ok(None),
107 Strictness::AllOrError => Err(error::Transition::TransitionImpossible(
108 rust_fsm::TransitionImpossibleError,
109 )
110 .into()),
111 },
112 }
113 }
114}
115
116#[derive_where(Debug, Clone, Serialize, Deserialize; FSM::State)]
117#[derive(spru::action::Update)]
118#[must_use]
119pub struct Set<FSM: StateMachineTy> {
120 new_state: FSM::State,
121}
122
123impl<FSM> spru::action::Update for Set<FSM>
124where
125 FSM: StateMachineTy<State: Clone>,
126{
127 type T = Fsm<FSM>;
128 type Undo = Self;
129
130 #[allow(refining_impl_trait)]
131 fn update(&self, value: &mut Self::T) -> AnyResult<Self::Undo> {
132 let old_state = mem::replace(&mut value.0, self.new_state.clone());
133 Ok(Self {
134 new_state: old_state,
135 })
136 }
137}