1use alloc::vec::Vec;
10
11use crate::Machine;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct TransitionRecord<M, E, C> {
19 pub step: usize,
21 pub from: M,
23 pub to: M,
25 pub event: E,
27 pub commands: Vec<C>,
29 pub timestamp: std::time::SystemTime,
31}
32
33impl<M, E, C> TransitionRecord<M, E, C> {
34 #[must_use]
36 pub fn new(step: usize, from: M, to: M, event: E, commands: Vec<C>) -> Self {
37 Self {
38 step,
39 from,
40 to,
41 event,
42 commands,
43 timestamp: std::time::SystemTime::now(),
44 }
45 }
46}
47
48#[derive(Debug, Default)]
50pub struct InMemoryJournal<M, E, C> {
51 records: Vec<TransitionRecord<M, E, C>>,
52}
53
54impl<M, E, C> InMemoryJournal<M, E, C> {
55 #[must_use]
57 pub const fn new() -> Self {
58 Self {
59 records: Vec::new(),
60 }
61 }
62
63 #[must_use]
65 pub fn len(&self) -> usize {
66 self.records.len()
67 }
68
69 #[must_use]
71 pub fn is_empty(&self) -> bool {
72 self.records.is_empty()
73 }
74
75 #[must_use]
77 pub fn records(&self) -> &[TransitionRecord<M, E, C>] {
78 &self.records
79 }
80
81 pub fn append(&mut self, record: TransitionRecord<M, E, C>) {
83 self.records.push(record);
84 }
85
86 #[must_use]
88 pub fn last(&self) -> Option<&TransitionRecord<M, E, C>> {
89 self.records.last()
90 }
91
92 pub fn record_step(&mut self, from: &M, to: &M, event: &E, commands: &[C])
97 where
98 M: Clone,
99 E: Clone,
100 C: Clone,
101 {
102 let step = self.records.len();
103 self.append(TransitionRecord::new(
104 step,
105 from.clone(),
106 to.clone(),
107 event.clone(),
108 commands.to_vec(),
109 ));
110 }
111
112 pub fn transitions_from<'a>(
114 &'a self,
115 mode: &'a M,
116 ) -> impl Iterator<Item = &'a TransitionRecord<M, E, C>> + 'a
117 where
118 M: PartialEq,
119 {
120 self.records.iter().filter(move |r| &r.from == mode)
121 }
122
123 pub fn transitions_to<'a>(
125 &'a self,
126 mode: &'a M,
127 ) -> impl Iterator<Item = &'a TransitionRecord<M, E, C>> + 'a
128 where
129 M: PartialEq,
130 {
131 self.records.iter().filter(move |r| &r.to == mode)
132 }
133
134 pub fn replay<Mac>(&self, machine: &Mac, initial_mode: M) -> Result<M, ReplayError>
142 where
143 Mac: Machine<Mode = M, Event = E, Command = C>,
144 M: Clone + PartialEq,
145 C: PartialEq,
146 {
147 self.replay_with_error::<core::convert::Infallible, _>(machine, initial_mode)
148 }
149
150 pub fn replay_with_error<Err, Mac>(
160 &self,
161 machine: &Mac,
162 initial_mode: M,
163 ) -> Result<M, ReplayError>
164 where
165 Mac: Machine<Err, Mode = M, Event = E, Command = C>,
166 M: Clone + PartialEq,
167 C: PartialEq,
168 {
169 let mut mode = initial_mode;
170
171 for record in &self.records {
172 if mode != record.from {
173 return Err(ReplayError::new(record.step, ReplayMismatch::FromMode));
174 }
175
176 let decision = machine.decide(&mode, &record.event);
177 let (next_mode, commands) = decision.apply(mode.clone());
178
179 if next_mode != record.to {
180 return Err(ReplayError::new(record.step, ReplayMismatch::ToMode));
181 }
182
183 if commands.as_slice() != record.commands.as_slice() {
184 return Err(ReplayError::new(record.step, ReplayMismatch::Commands));
185 }
186
187 mode = next_mode;
188 }
189
190 Ok(mode)
191 }
192}
193
194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
205#[non_exhaustive]
206pub enum ReplayMismatch {
207 FromMode,
209 ToMode,
211 Commands,
213}
214
215impl core::fmt::Display for ReplayMismatch {
216 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
217 match self {
218 Self::FromMode => {
219 write!(f, "current mode differs from recorded starting mode")
220 }
221 Self::ToMode => {
222 write!(f, "computed next mode differs from recorded mode")
223 }
224 Self::Commands => {
225 write!(f, "computed commands differ from recorded commands")
226 }
227 }
228 }
229}
230
231#[derive(Debug, Clone, PartialEq, Eq)]
247pub struct ReplayError {
248 step: usize,
249 mismatch: ReplayMismatch,
250}
251
252impl core::fmt::Display for ReplayError {
253 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
254 write!(
255 f,
256 "replay diverged at step {}: {}",
257 self.step, self.mismatch,
258 )
259 }
260}
261
262impl std::error::Error for ReplayError {}
263
264impl ReplayError {
265 #[must_use]
267 pub const fn new(step: usize, mismatch: ReplayMismatch) -> Self {
268 Self { step, mismatch }
269 }
270
271 #[must_use]
273 pub const fn step(&self) -> usize {
274 self.step
275 }
276
277 #[must_use]
279 pub const fn mismatch(&self) -> ReplayMismatch {
280 self.mismatch
281 }
282}