1use std::sync::atomic::{AtomicU32, Ordering};
8
9use serde::{Deserialize, Serialize};
10
11macro_rules! define_id_type {
12 ($name:ident) => {
13 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
14 #[serde(transparent)]
15 pub struct $name(pub u32);
16
17 impl $name {
18 pub fn to_raw(self) -> u32 {
19 self.0
20 }
21 }
22 };
23}
24
25define_id_type!(FileId);
26define_id_type!(SymbolId);
27define_id_type!(SnapshotId);
28
29#[derive(Debug)]
30pub struct IdGenerator<T> {
31 counter: AtomicU32,
32 _marker: std::marker::PhantomData<T>,
33}
34
35impl<T> IdGenerator<T> {
36 pub fn new() -> Self {
37 Self {
38 counter: AtomicU32::new(0),
39 _marker: std::marker::PhantomData,
40 }
41 }
42
43 pub fn next(&self) -> T
44 where
45 T: From<u32>,
46 {
47 let val = self.counter.fetch_add(1, Ordering::SeqCst);
48 T::from(val)
49 }
50}
51
52impl<T> Default for IdGenerator<T> {
53 fn default() -> Self {
54 Self::new()
55 }
56}
57
58impl From<u32> for FileId {
59 fn from(val: u32) -> Self {
60 Self(val)
61 }
62}
63
64impl From<u32> for SymbolId {
65 fn from(val: u32) -> Self {
66 Self(val)
67 }
68}
69
70impl From<u32> for SnapshotId {
71 fn from(val: u32) -> Self {
72 Self(val)
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use std::collections::HashSet;
80 use std::sync::Arc;
81
82 #[test]
83 fn file_id_sequential() {
84 let idgen = IdGenerator::<FileId>::new();
85 assert_eq!(idgen.next(), FileId(0));
86 assert_eq!(idgen.next(), FileId(1));
87 assert_eq!(idgen.next(), FileId(2));
88 }
89
90 #[test]
91 fn symbol_id_sequential() {
92 let idgen = IdGenerator::<SymbolId>::new();
93 assert_eq!(idgen.next(), SymbolId(0));
94 assert_eq!(idgen.next(), SymbolId(1));
95 assert_eq!(idgen.next(), SymbolId(2));
96 }
97
98 #[test]
99 fn snapshot_id_sequential() {
100 let idgen = IdGenerator::<SnapshotId>::new();
101 assert_eq!(idgen.next(), SnapshotId(0));
102 assert_eq!(idgen.next(), SnapshotId(1));
103 assert_eq!(idgen.next(), SnapshotId(2));
104 }
105
106 #[test]
107 fn id_generator_thread_safe() {
108 let idgen = Arc::new(IdGenerator::<SymbolId>::new());
109 let mut handles = Vec::new();
110
111 for _ in 0..4 {
112 let idgen = Arc::clone(&idgen);
113 handles.push(std::thread::spawn(move || {
114 let mut ids = Vec::with_capacity(250);
115 for _ in 0..250 {
116 ids.push(idgen.next());
117 }
118 ids
119 }));
120 }
121
122 let all_ids: Vec<SymbolId> = handles
123 .into_iter()
124 .flat_map(|h| h.join().unwrap_or_default())
125 .collect();
126
127 let unique: HashSet<SymbolId> = all_ids.iter().copied().collect();
128 assert_eq!(unique.len(), 1000, "expected 1000 unique IDs");
129 assert!(
130 unique.iter().all(|id| id.to_raw() < 1000),
131 "all IDs must be in range 0..1000"
132 );
133 }
134
135 #[test]
136 fn file_id_serde_roundtrip() {
137 let original = FileId(42);
138 let json = serde_json::to_string(&original).unwrap();
139 let roundtrip: FileId = serde_json::from_str(&json).unwrap();
140 assert_eq!(original, roundtrip);
141 }
142
143 #[test]
144 fn symbol_id_serde_roundtrip() {
145 let original = SymbolId(99);
146 let json = serde_json::to_string(&original).unwrap();
147 let roundtrip: SymbolId = serde_json::from_str(&json).unwrap();
148 assert_eq!(original, roundtrip);
149 }
150
151 #[test]
152 fn file_id_to_raw() {
153 assert_eq!(FileId(7).to_raw(), 7);
154 }
155
156 #[test]
157 fn id_generator_default() {
158 let idgen = IdGenerator::<FileId>::default();
159 assert_eq!(idgen.next(), FileId(0));
160 }
161}