Skip to main content

omena_reactive/
divergence.rs

1/// Reviewed mismatch classes for observations made before a wave flushes.
2#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
3#[non_exhaustive]
4pub enum ReactiveDivergenceClassV0 {
5    FlushConeClosureTiming,
6    MidWaveReadTiming,
7}
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10#[non_exhaustive]
11pub enum ReactiveObservationPhaseV0 {
12    DuringWave,
13    Flush,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17#[non_exhaustive]
18pub enum ReactiveDivergenceDispositionV0 {
19    BenignUntilFlush,
20    Blocker,
21}
22
23impl ReactiveDivergenceClassV0 {
24    /// Returns the reviewed classes without exposing their count in the type.
25    pub const fn all() -> &'static [Self] {
26        &[Self::FlushConeClosureTiming, Self::MidWaveReadTiming]
27    }
28
29    pub const fn id(self) -> &'static str {
30        match self {
31            Self::FlushConeClosureTiming => "flushConeClosureTiming",
32            Self::MidWaveReadTiming => "midWaveReadTiming",
33        }
34    }
35
36    pub fn from_id(id: &str) -> Option<Self> {
37        Self::all().iter().copied().find(|class| class.id() == id)
38    }
39
40    /// A reviewed timing difference is benign only while the graph is still
41    /// stabilizing. Any mismatch that survives the flush remains a blocker.
42    pub const fn disposition(
43        self,
44        phase: ReactiveObservationPhaseV0,
45    ) -> ReactiveDivergenceDispositionV0 {
46        match phase {
47            ReactiveObservationPhaseV0::DuringWave => {
48                ReactiveDivergenceDispositionV0::BenignUntilFlush
49            }
50            ReactiveObservationPhaseV0::Flush => ReactiveDivergenceDispositionV0::Blocker,
51        }
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    const TAXONOMY: &str = include_str!("../docs/divergence-taxonomy.md");
60
61    #[test]
62    fn committed_taxonomy_and_machine_ids_are_in_lockstep() {
63        for class in ReactiveDivergenceClassV0::all() {
64            assert!(
65                TAXONOMY.contains(&format!("`{}`", class.id())),
66                "taxonomy must document {}",
67                class.id()
68            );
69        }
70        assert_eq!(TAXONOMY.matches("## Class:").count(), 2);
71    }
72
73    #[test]
74    fn reviewed_timing_classes_never_suppress_a_flush_mismatch() {
75        for class in ReactiveDivergenceClassV0::all() {
76            assert_eq!(
77                class.disposition(ReactiveObservationPhaseV0::DuringWave),
78                ReactiveDivergenceDispositionV0::BenignUntilFlush
79            );
80            assert_eq!(
81                class.disposition(ReactiveObservationPhaseV0::Flush),
82                ReactiveDivergenceDispositionV0::Blocker
83            );
84        }
85        assert!(ReactiveDivergenceClassV0::from_id("unreviewedDifference").is_none());
86    }
87}