wickra_exchange_core/
idempotency.rs1use std::sync::atomic::{AtomicU64, Ordering};
12
13#[derive(Debug)]
16pub struct ClientIdGenerator {
17 prefix: String,
18 counter: AtomicU64,
19}
20
21impl ClientIdGenerator {
22 pub fn new(prefix: impl Into<String>) -> Self {
24 Self {
25 prefix: prefix.into(),
26 counter: AtomicU64::new(0),
27 }
28 }
29
30 pub fn with_seed(prefix: impl Into<String>, seed: u64) -> Self {
32 Self {
33 prefix: prefix.into(),
34 counter: AtomicU64::new(seed),
35 }
36 }
37
38 pub fn next_id(&self) -> String {
40 let n = self.counter.fetch_add(1, Ordering::Relaxed);
41 format!("{}-{n}", self.prefix)
42 }
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48 use std::collections::HashSet;
49
50 #[test]
51 fn ids_carry_the_prefix_and_increment() {
52 let gen = ClientIdGenerator::new("wkex");
53 assert_eq!(gen.next_id(), "wkex-0");
54 assert_eq!(gen.next_id(), "wkex-1");
55 assert_eq!(gen.next_id(), "wkex-2");
56 }
57
58 #[test]
59 fn seed_makes_the_sequence_reproducible() {
60 let gen = ClientIdGenerator::with_seed("x", 100);
61 assert_eq!(gen.next_id(), "x-100");
62 assert_eq!(gen.next_id(), "x-101");
63 }
64
65 #[test]
66 fn ids_are_unique_over_a_run() {
67 let gen = ClientIdGenerator::new("p");
68 let ids: HashSet<String> = (0..1000).map(|_| gen.next_id()).collect();
69 assert_eq!(ids.len(), 1000);
70 }
71}