Skip to main content

omni_dev/voice/
det.rs

1//! Pluggable RNG for [`EventId`](super::EventId) (ULID) generation.
2//!
3//! Lives at `voice/` scope rather than under `backends/mock.rs` because
4//! the same trait is consumed by [`voice reflect`](super::reflect) for
5//! deterministic event minting in snapshot tests. Production code uses
6//! [`SystemUlidRng`]; tests use [`CountingUlidRng`].
7
8use ulid::Ulid;
9
10/// Source of [`Ulid`]s. Pluggable so tests can pin ULIDs while production
11/// uses real entropy.
12pub trait UlidRng: Send + Sync {
13    /// Returns the next ULID. Each call must produce a value strictly
14    /// greater than the previous one to preserve event-id monotonicity.
15    fn next_ulid(&mut self) -> Ulid;
16}
17
18/// Production RNG: defers to `Ulid::new()`.
19#[derive(Debug, Default)]
20pub struct SystemUlidRng;
21
22impl UlidRng for SystemUlidRng {
23    fn next_ulid(&mut self) -> Ulid {
24        Ulid::new()
25    }
26}
27
28/// Deterministic RNG for tests.
29///
30/// Returns `Ulid::from_parts(0, counter)` for an increasing counter
31/// starting at 1. The encoded form is lexicographically ordered, so a
32/// sequence of ULIDs from this RNG is monotonically increasing — exactly
33/// the property snapshot tests rely on.
34#[derive(Debug, Default)]
35pub struct CountingUlidRng {
36    counter: u128,
37}
38
39impl CountingUlidRng {
40    /// Builds a new counting RNG starting at zero — the first ULID it
41    /// returns has random bits `1`, the second `2`, and so on.
42    pub fn new() -> Self {
43        Self { counter: 0 }
44    }
45}
46
47impl UlidRng for CountingUlidRng {
48    fn next_ulid(&mut self) -> Ulid {
49        self.counter += 1;
50        Ulid::from_parts(0, self.counter)
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn system_ulid_rng_produces_unique_values() {
60        let mut rng = SystemUlidRng;
61        let a = rng.next_ulid();
62        let b = rng.next_ulid();
63        assert_ne!(a, b);
64    }
65
66    #[test]
67    fn counting_ulid_rng_starts_at_one() {
68        let mut rng = CountingUlidRng::new();
69        let first = rng.next_ulid();
70        assert_eq!(first, Ulid::from_parts(0, 1));
71    }
72
73    #[test]
74    fn counting_ulid_rng_is_monotonic() {
75        let mut rng = CountingUlidRng::new();
76        let a = rng.next_ulid();
77        let b = rng.next_ulid();
78        let c = rng.next_ulid();
79        assert!(a < b);
80        assert!(b < c);
81    }
82}