1pub use rust_automata_macros::state_machine;
6pub use rust_automata_macros::Display;
7
8#[doc(hidden)]
9#[cfg(feature = "mermaid")]
10pub use aquamarine::aquamarine;
11
12pub mod clock;
13#[doc(hidden)]
14mod takeable;
15pub mod timestamp;
16
17use core::fmt::Display;
18use std::hash::Hash;
19use std::marker::PhantomData;
20
21#[doc(hidden)]
22pub use takeable::Takeable;
23
24pub trait Alphabet: Display {
28 fn nothing() -> Self;
30 fn any(&self) -> bool;
32}
33
34pub trait StateTrait: Display {
38 fn failure() -> Self;
39 fn is_failure(&self) -> bool;
40}
41
42#[doc(hidden)]
44pub trait Enumerable<ForEnum> {
45 fn enum_id(&self) -> EnumId<ForEnum>;
46}
47
48#[doc(hidden)]
50pub trait Enumerated<InEnum> {
51 fn enum_id() -> EnumId<InEnum>;
52}
53
54#[doc(hidden)]
56#[derive(Clone, Copy, Debug, Eq, PartialOrd, Ord, Default)]
57pub struct EnumId<ForEnum> {
58 pub id: usize,
59 _marker: PhantomData<ForEnum>,
60}
61
62impl<ForEnum> PartialEq for EnumId<ForEnum> {
63 fn eq(&self, other: &Self) -> bool {
64 self.id == other.id
65 }
66}
67
68impl<ForEnum> Hash for EnumId<ForEnum> {
69 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
70 self.id.hash(state);
71 }
72}
73
74impl<ForEnum> EnumId<ForEnum> {
75 pub fn new(id: usize) -> Self {
76 EnumId {
77 id,
78 _marker: PhantomData,
79 }
80 }
81}
82
83#[doc(hidden)]
90pub trait StateMachineImpl {
91 type Input: Alphabet;
93 type State: StateTrait + Enumerable<Self::State>;
95 type Output: Alphabet;
97 type InitialState: Enumerated<Self::State> + Into<Self::State>;
99 fn transition(
102 &mut self,
103 state: Takeable<Self::State>,
104 input: Self::Input,
105 ) -> (Takeable<Self::State>, Self::Output);
106 fn can_transition(
108 &self,
109 state: &Self::State,
110 input: EnumId<Self::Input>,
111 ) -> Option<EnumId<Self::Output>>;
112}
113
114pub struct StateMachine<T: StateMachineImpl> {
116 state: Takeable<T::State>,
117 data: T,
118}
119
120impl<T> StateMachine<T>
121where
122 T: StateMachineImpl,
123{
124 pub fn new(data: T, initial_state: T::InitialState) -> Self {
126 Self {
127 state: Takeable::new(initial_state.into()),
128 data,
129 }
130 }
131
132 pub fn step(&mut self) {
134 let _: T::Output = self.relay::<T::Input, T::Output>(T::Input::nothing());
135 }
136
137 pub fn produce<O: From<T::Output>>(&mut self) -> O {
139 self.relay::<T::Input, O>(T::Input::nothing())
140 }
141
142 pub fn consume<I: Into<T::Input>>(&mut self, input: I) {
144 let _: T::Output = self.relay::<I, T::Output>(input);
145 }
146
147 pub fn relay<I: Into<T::Input>, O: From<T::Output>>(&mut self, input: I) -> O {
149 let enum_input = input.into();
150 let from_str = self.state.as_ref().to_string();
151 let input_str = enum_input.to_string();
152
153 let current_state = std::mem::replace(&mut self.state, Takeable::new(T::State::failure()));
155
156 let (next_state, output) = self.data.transition(current_state, enum_input);
158
159 self.state = next_state;
161
162 if self.state.is_failure() {
163 panic!(
164 "Invalid transition from {} using input {}",
165 from_str, input_str
166 );
167 }
168
169 O::from(output)
170 }
171
172 pub fn can_step(&mut self) -> bool {
173 let enum_input = EnumId::new(0);
174 let enum_state = self.state.as_ref();
175 let actual_output = self.data.can_transition(enum_state, enum_input);
176 actual_output.is_some()
177 }
178
179 pub fn can_produce<O>(&mut self) -> bool
180 where
181 O: Enumerated<T::Output>,
182 {
183 let enum_input = EnumId::new(0);
184 let enum_state = self.state.as_ref();
185 let actual_output = self.data.can_transition(enum_state, enum_input);
186 let expected_enum = O::enum_id();
187 match actual_output {
188 Some(enum_output) => enum_output == expected_enum,
189 None => false,
190 }
191 }
192
193 pub fn can_consume<I>(&mut self) -> bool
194 where
195 I: Enumerated<T::Input>,
196 {
197 let enum_input = I::enum_id();
198 let enum_state = self.state.as_ref();
199 let actual_output = self.data.can_transition(enum_state, enum_input);
200 actual_output.is_some()
201 }
202
203 pub fn can_relay<I, O>(&mut self) -> bool
204 where
205 I: Enumerated<T::Input>,
206 O: Enumerated<T::Output>,
207 {
208 let enum_input = I::enum_id();
209 let enum_state = self.state.as_ref();
210 let actual_output = self.data.can_transition(enum_state, enum_input);
211 let expected_enum = O::enum_id();
212 match actual_output {
213 Some(enum_output) => enum_output == expected_enum,
214 None => false,
215 }
216 }
217
218 pub fn state(&self) -> &T::State {
220 &self.state
221 }
222
223 pub fn data(&self) -> &T {
224 &self.data
225 }
226}