1#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
5pub struct VertexId(pub u8);
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub struct TriggerId(pub u8);
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct Vertex {
14 pub id: VertexId,
16 pub label: &'static str,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct Transition {
23 pub from: VertexId,
25 pub trigger: TriggerId,
27 pub to: VertexId,
29 pub label: &'static str,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub struct Topology {
36 pub name: &'static str,
38 pub initial: VertexId,
40 pub vertices: &'static [Vertex],
42 pub transitions: &'static [Transition],
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub struct ValidatedTopology(Topology);
49
50impl ValidatedTopology {
51 #[must_use]
53 pub const fn topology(self) -> Topology {
54 self.0
55 }
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
60pub enum TopologyError {
61 #[error("two vertices share an identity")]
63 DuplicateVertex(VertexId),
64 #[error("two transitions share a source and trigger")]
66 DuplicateTransition(Transition),
67 #[error("initial identity is not a declared vertex")]
69 UnknownInitial(VertexId),
70 #[error("transition refers to an undeclared vertex")]
72 UnknownVertex(VertexId),
73 #[error("declared vertex is unreachable from the initial vertex")]
75 UnreachableVertex(VertexId),
76}
77
78impl Topology {
79 pub fn validated(self) -> Result<ValidatedTopology, TopologyError> {
85 self.validate()?;
86 Ok(ValidatedTopology(self))
87 }
88
89 pub fn validate(self) -> Result<(), TopologyError> {
99 for (index, vertex) in self.vertices.iter().enumerate() {
100 if self.vertices[..index]
101 .iter()
102 .any(|known| known.id == vertex.id)
103 {
104 return Err(TopologyError::DuplicateVertex(vertex.id));
105 }
106 }
107 if !self.vertices.iter().any(|vertex| vertex.id == self.initial) {
108 return Err(TopologyError::UnknownInitial(self.initial));
109 }
110 for (index, edge) in self.transitions.iter().enumerate() {
111 if self.transitions[..index]
112 .iter()
113 .any(|known| known.from == edge.from && known.trigger == edge.trigger)
114 {
115 return Err(TopologyError::DuplicateTransition(*edge));
116 }
117 for vertex in [edge.from, edge.to] {
118 if !self.vertices.iter().any(|known| known.id == vertex) {
119 return Err(TopologyError::UnknownVertex(vertex));
120 }
121 }
122 }
123 let mut reachable = [false; 256];
124 reachable[usize::from(self.initial.0)] = true;
125 for _ in 0..self.vertices.len() {
126 for edge in self.transitions {
127 if reachable[usize::from(edge.from.0)] {
128 reachable[usize::from(edge.to.0)] = true;
129 }
130 }
131 }
132 self.vertices
133 .iter()
134 .find(|vertex| !reachable[usize::from(vertex.id.0)])
135 .map_or(Ok(()), |vertex| {
136 Err(TopologyError::UnreachableVertex(vertex.id))
137 })
138 }
139
140 pub fn write_mermaid(self, output: &mut impl core::fmt::Write) -> core::fmt::Result {
147 writeln!(output, "stateDiagram-v2")?;
148 let initial = self.label(self.initial).ok_or(core::fmt::Error)?;
149 writeln!(output, " [*] --> {initial}")?;
150 self.transitions.iter().try_for_each(|edge| {
151 let from = self.label(edge.from).ok_or(core::fmt::Error)?;
152 let to = self.label(edge.to).ok_or(core::fmt::Error)?;
153 writeln!(output, " {from} --> {to}: {}", edge.label)
154 })
155 }
156
157 fn label(self, id: VertexId) -> Option<&'static str> {
158 self.vertices
159 .iter()
160 .find(|vertex| vertex.id == id)
161 .map(|vertex| vertex.label)
162 }
163}
164
165pub trait Machine {
167 type Input;
169
170 type Output;
172
173 fn step(self, input: Self::Input) -> (Self::Output, Self)
175 where
176 Self: Sized;
177
178 fn describe<V: Structure>(&self, visitor: &mut V) -> V::Output;
180}
181
182pub trait Compose: Machine + Sized {
184 fn then<N>(self, next: N) -> Then<Self, N> {
186 Then(self, next)
187 }
188
189 fn product<N>(self, other: N) -> Product<Self, N> {
191 Product(self, other)
192 }
193
194 fn routed<N>(self, other: N) -> Routed<Self, N> {
196 Routed(self, other)
197 }
198}
199
200impl<M: Machine> Compose for M {}
201
202pub trait Structure {
204 type Output;
206
207 fn base(&mut self, topology: Topology) -> Self::Output;
209
210 fn then(&mut self, first: Self::Output, second: Self::Output) -> Self::Output;
212
213 fn product(&mut self, left: Self::Output, right: Self::Output) -> Self::Output;
215
216 fn routed(&mut self, left: Self::Output, right: Self::Output) -> Self::Output;
218}
219
220pub struct Base<S, F, I, O> {
222 state: S,
223 transition: F,
224 topology: ValidatedTopology,
225 signature: core::marker::PhantomData<fn(I) -> O>,
226}
227
228impl<S, F, I, O> Base<S, F, I, O> {
229 pub const fn new(state: S, topology: ValidatedTopology, transition: F) -> Self {
231 Self {
232 state,
233 transition,
234 topology,
235 signature: core::marker::PhantomData,
236 }
237 }
238
239 #[must_use]
241 pub const fn state(&self) -> &S {
242 &self.state
243 }
244}
245
246impl<S, I, O, F> Machine for Base<S, F, I, O>
247where
248 F: FnMut(S, I) -> (O, S),
249{
250 type Input = I;
251 type Output = O;
252
253 fn step(self, input: Self::Input) -> (Self::Output, Self) {
254 let Self {
255 state,
256 mut transition,
257 topology,
258 signature,
259 } = self;
260 let (output, state) = transition(state, input);
261 (
262 output,
263 Self {
264 state,
265 transition,
266 topology,
267 signature,
268 },
269 )
270 }
271
272 fn describe<V: Structure>(&self, visitor: &mut V) -> V::Output {
273 visitor.base(self.topology.topology())
274 }
275}
276
277pub struct Then<A, B>(A, B);
279
280impl<A, B> Machine for Then<A, B>
281where
282 A: Machine,
283 B: Machine<Input = A::Output>,
284{
285 type Input = A::Input;
286 type Output = B::Output;
287
288 fn step(self, input: Self::Input) -> (Self::Output, Self) {
289 let (middle, first) = self.0.step(input);
290 let (output, second) = self.1.step(middle);
291 (output, Self(first, second))
292 }
293
294 fn describe<V: Structure>(&self, visitor: &mut V) -> V::Output {
295 let first = self.0.describe(visitor);
296 let second = self.1.describe(visitor);
297 visitor.then(first, second)
298 }
299}
300
301pub struct Product<A, B>(A, B);
303
304impl<A, B> Machine for Product<A, B>
305where
306 A: Machine,
307 B: Machine,
308{
309 type Input = (A::Input, B::Input);
310 type Output = (A::Output, B::Output);
311
312 fn step(self, (left, right): Self::Input) -> (Self::Output, Self) {
313 let (left_output, left_machine) = self.0.step(left);
314 let (right_output, right_machine) = self.1.step(right);
315 (
316 (left_output, right_output),
317 Self(left_machine, right_machine),
318 )
319 }
320
321 fn describe<V: Structure>(&self, visitor: &mut V) -> V::Output {
322 let left = self.0.describe(visitor);
323 let right = self.1.describe(visitor);
324 visitor.product(left, right)
325 }
326}
327
328#[derive(Debug, Clone, Copy, PartialEq, Eq)]
330pub enum Either<L, R> {
331 Left(L),
333 Right(R),
335}
336
337pub struct Routed<A, B>(A, B);
339
340impl<A, B> Machine for Routed<A, B>
341where
342 A: Machine,
343 B: Machine,
344{
345 type Input = Either<A::Input, B::Input>;
346 type Output = Either<A::Output, B::Output>;
347
348 fn step(self, input: Self::Input) -> (Self::Output, Self) {
349 match input {
350 Either::Left(left) => {
351 let (output, machine) = self.0.step(left);
352 (Either::Left(output), Self(machine, self.1))
353 }
354 Either::Right(right) => {
355 let (output, machine) = self.1.step(right);
356 (Either::Right(output), Self(self.0, machine))
357 }
358 }
359 }
360
361 fn describe<V: Structure>(&self, visitor: &mut V) -> V::Output {
362 let left = self.0.describe(visitor);
363 let right = self.1.describe(visitor);
364 visitor.routed(left, right)
365 }
366}
367
368#[cfg(test)]
369mod tests {
370 use alloc::string::ToString;
371
372 use super::{
373 Base, Compose, Machine, Structure, Topology, TopologyError, Transition, TriggerId, Vertex,
374 VertexId,
375 };
376
377 #[test]
378 fn topology_error_reports_each_defect() {
379 assert_eq!(
380 TopologyError::DuplicateVertex(READY).to_string(),
381 "two vertices share an identity"
382 );
383 assert_eq!(
384 TopologyError::UnknownInitial(READY).to_string(),
385 "initial identity is not a declared vertex"
386 );
387 assert_eq!(
388 TopologyError::UnknownVertex(READY).to_string(),
389 "transition refers to an undeclared vertex"
390 );
391 assert_eq!(
392 TopologyError::UnreachableVertex(READY).to_string(),
393 "declared vertex is unreachable from the initial vertex"
394 );
395 assert_eq!(
396 TopologyError::DuplicateTransition(EDGES[0]).to_string(),
397 "two transitions share a source and trigger"
398 );
399 }
400
401 const READY: VertexId = VertexId(0);
402 const VERTICES: &[Vertex] = &[Vertex {
403 id: READY,
404 label: "ready",
405 }];
406
407 const EDGES: &[Transition] = &[Transition {
408 from: READY,
409 trigger: TriggerId(0),
410 to: READY,
411 label: "advance",
412 }];
413 const UNKNOWN: VertexId = VertexId(9);
414 const UNKNOWN_EDGE: &[Transition] = &[Transition {
415 from: READY,
416 trigger: TriggerId(1),
417 to: UNKNOWN,
418 label: "lost",
419 }];
420 const STRANDED: VertexId = VertexId(1);
421 const STRANDED_VERTICES: &[Vertex] = &[
422 VERTICES[0],
423 Vertex {
424 id: STRANDED,
425 label: "stranded",
426 },
427 ];
428 const DUPLICATE_EDGES: &[Transition] = &[EDGES[0], EDGES[0]];
429 const AMBIGUOUS: &[Transition] = &[
430 EDGES[0],
431 Transition {
432 from: READY,
433 trigger: TriggerId(0),
434 to: STRANDED,
435 label: "different presentation",
436 },
437 ];
438
439 fn topology(name: &'static str) -> Topology {
440 Topology {
441 name,
442 initial: READY,
443 vertices: VERTICES,
444 transitions: EDGES,
445 }
446 }
447
448 #[derive(Debug, PartialEq, Eq)]
449 struct Shape {
450 bases: usize,
451 sequences: usize,
452 products: usize,
453 choices: usize,
454 }
455
456 struct Count;
457
458 impl Structure for Count {
459 type Output = Shape;
460
461 fn base(&mut self, _topology: Topology) -> Self::Output {
462 Shape {
463 bases: 1,
464 sequences: 0,
465 products: 0,
466 choices: 0,
467 }
468 }
469
470 fn then(&mut self, first: Self::Output, second: Self::Output) -> Self::Output {
471 Shape {
472 bases: first.bases + second.bases,
473 sequences: first.sequences + second.sequences + 1,
474 products: first.products + second.products,
475 choices: first.choices + second.choices,
476 }
477 }
478
479 fn product(&mut self, left: Self::Output, right: Self::Output) -> Self::Output {
480 Shape {
481 bases: left.bases + right.bases,
482 sequences: left.sequences + right.sequences,
483 products: left.products + right.products + 1,
484 choices: left.choices + right.choices,
485 }
486 }
487
488 fn routed(&mut self, left: Self::Output, right: Self::Output) -> Self::Output {
489 Shape {
490 bases: left.bases + right.bases,
491 sequences: left.sequences + right.sequences,
492 products: left.products + right.products,
493 choices: left.choices + right.choices + 1,
494 }
495 }
496 }
497
498 #[test]
499 fn sequential_machine_executes_and_retains_its_structure() {
500 let increment = Base::new(
501 0_u8,
502 topology("increment").validated().unwrap(),
503 |state: u8, input: u8| {
504 let state = state + input;
505 (state, state)
506 },
507 );
508 let double = Base::new(
509 (),
510 topology("double").validated().unwrap(),
511 |(), input: u8| (input * 2, ()),
512 );
513 let machine = increment.then(double);
514
515 let (output, machine) = machine.step(3);
516 assert_eq!(output, 6);
517 let (output, machine) = machine.step(1);
518 assert_eq!(output, 8);
519 assert_eq!(
520 machine.describe(&mut Count),
521 Shape {
522 bases: 2,
523 sequences: 1,
524 products: 0,
525 choices: 0,
526 }
527 );
528 }
529
530 #[test]
531 fn topology_validation_rejects_each_structural_defect() {
532 const DUPLICATE_VERTICES: &[Vertex] = &[
533 Vertex {
534 id: READY,
535 label: "ready",
536 },
537 Vertex {
538 id: READY,
539 label: "again",
540 },
541 ];
542 assert_eq!(
543 Topology {
544 name: "duplicate",
545 initial: READY,
546 vertices: DUPLICATE_VERTICES,
547 transitions: &[]
548 }
549 .validate(),
550 Err(super::TopologyError::DuplicateVertex(READY))
551 );
552
553 assert_eq!(
554 Topology {
555 name: "initial",
556 initial: UNKNOWN,
557 vertices: VERTICES,
558 transitions: &[]
559 }
560 .validate(),
561 Err(super::TopologyError::UnknownInitial(UNKNOWN))
562 );
563
564 assert_eq!(
565 Topology {
566 name: "reference",
567 initial: READY,
568 vertices: VERTICES,
569 transitions: UNKNOWN_EDGE
570 }
571 .validate(),
572 Err(super::TopologyError::UnknownVertex(UNKNOWN))
573 );
574
575 assert_eq!(
576 Topology {
577 name: "reachability",
578 initial: READY,
579 vertices: STRANDED_VERTICES,
580 transitions: &[]
581 }
582 .validate(),
583 Err(super::TopologyError::UnreachableVertex(STRANDED))
584 );
585
586 assert_eq!(
587 Topology {
588 name: "edge",
589 initial: READY,
590 vertices: VERTICES,
591 transitions: DUPLICATE_EDGES
592 }
593 .validate(),
594 Err(super::TopologyError::DuplicateTransition(EDGES[0]))
595 );
596
597 assert_eq!(
598 Topology {
599 name: "ambiguous",
600 initial: READY,
601 vertices: STRANDED_VERTICES,
602 transitions: AMBIGUOUS,
603 }
604 .validate(),
605 Err(super::TopologyError::DuplicateTransition(AMBIGUOUS[1]))
606 );
607 }
608
609 #[test]
610 fn product_and_routed_composition_preserve_untouched_affine_state() {
611 struct Affine(u8);
612
613 let left = Base::new(
614 Affine(1),
615 topology("left").validated().unwrap(),
616 |state: Affine, input: u8| (state.0 + input, Affine(state.0 + input)),
617 );
618 let right = Base::new(
619 Affine(10),
620 topology("right").validated().unwrap(),
621 |state: Affine, input: u8| (state.0 + input, Affine(state.0 + input)),
622 );
623 let (output, product) = left.product(right).step((2, 3));
624 assert_eq!(output, (3, 13));
625
626 let left = Base::new(
627 Affine(1),
628 topology("left").validated().unwrap(),
629 |state: Affine, input: u8| (state.0 + input, Affine(state.0 + input)),
630 );
631 let right = Base::new(
632 Affine(10),
633 topology("right").validated().unwrap(),
634 |state: Affine, input: u8| (state.0 + input, Affine(state.0 + input)),
635 );
636 let (output, routed) = left.routed(right).step(super::Either::Left(2));
637 assert_eq!(output, super::Either::Left(3));
638 let (output, _) = routed.step(super::Either::Right(3));
639 assert_eq!(output, super::Either::Right(13));
640 assert_eq!(product.describe(&mut Count).products, 1);
641 }
642}