Skip to main content

coven_core/
id_provider.rs

1//! Identifier source, injected so tests get a deterministic — but still
2//! unique — id sequence.
3//!
4//! Production wires [`UuidProvider`] (`Uuid::new_v4().to_string()`); tests
5//! construct a [`SequentialIdProvider`] and pass it to the unit under test.
6//!
7//! Each `new_id()` call yields a fresh, distinct id. Per-entity uniqueness is
8//! preserved: a loop minting one id per row keeps producing distinct ids under
9//! both the production and the test provider.
10
11use std::sync::Arc;
12use uuid::Uuid;
13
14/// Identifier source. Yields a fresh unique id per call.
15pub trait IdProvider: Send + Sync {
16    /// A fresh unique identifier as a string.
17    fn new_id(&self) -> String;
18}
19
20/// Shared handle to an id provider. Held by `Clone` types that need to share
21/// one id source, so they clone the handle, not the implementation.
22pub type IdRef = Arc<dyn IdProvider>;
23
24/// Production provider: random v4 UUIDs.
25pub struct UuidProvider;
26
27impl IdProvider for UuidProvider {
28    fn new_id(&self) -> String {
29        Uuid::new_v4().to_string()
30    }
31}
32
33// The deterministic id fake is exposed to downstream crates' tests via the
34// `test-utils` feature, so any crate that consumes `IdProvider` tests against
35// the same fake instead of mirroring it.
36#[cfg(any(test, feature = "test-utils"))]
37pub use fakes::SequentialIdProvider;
38
39#[cfg(any(test, feature = "test-utils"))]
40mod fakes {
41    use super::*;
42    use std::sync::atomic::{AtomicU64, Ordering};
43
44    /// Deterministic but unique: `"{prefix}-0"`, `"{prefix}-1"`, ... Preserves
45    /// the per-entity uniqueness invariant while being reproducible across runs.
46    pub struct SequentialIdProvider {
47        prefix: String,
48        next: AtomicU64,
49    }
50
51    impl SequentialIdProvider {
52        pub fn new(prefix: &str) -> Self {
53            Self {
54                prefix: prefix.to_string(),
55                next: AtomicU64::new(0),
56            }
57        }
58    }
59
60    impl IdProvider for SequentialIdProvider {
61        fn new_id(&self) -> String {
62            let n = self.next.fetch_add(1, Ordering::SeqCst);
63            format!("{}-{}", self.prefix, n)
64        }
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn sequential_ids_are_deterministic_and_unique() {
74        let ids = SequentialIdProvider::new("id");
75        assert_eq!(ids.new_id(), "id-0");
76        assert_eq!(ids.new_id(), "id-1");
77        assert_eq!(ids.new_id(), "id-2");
78    }
79
80    #[test]
81    fn provider_is_usable_behind_the_shared_handle() {
82        let ids: IdRef = Arc::new(SequentialIdProvider::new("row"));
83        assert_eq!(ids.new_id(), "row-0");
84        assert_ne!(ids.new_id(), ids.new_id());
85    }
86}