1use std::{
19 collections::HashMap,
20 sync::{LazyLock, OnceLock},
21};
22
23use parking_lot::Mutex;
24
25use crate::identifiers::Venue;
26
27static CBCM_LOCK: OnceLock<Venue> = OnceLock::new();
28static GLBX_LOCK: OnceLock<Venue> = OnceLock::new();
29static NYUM_LOCK: OnceLock<Venue> = OnceLock::new();
30static XCBT_LOCK: OnceLock<Venue> = OnceLock::new();
31static XCEC_LOCK: OnceLock<Venue> = OnceLock::new();
32static XCME_LOCK: OnceLock<Venue> = OnceLock::new();
33static XFXS_LOCK: OnceLock<Venue> = OnceLock::new();
34static XNYM_LOCK: OnceLock<Venue> = OnceLock::new();
35
36impl Venue {
37 #[allow(non_snake_case)]
39 pub fn CBCM() -> Self {
40 *CBCM_LOCK.get_or_init(|| Self::from("CBCM"))
41 }
42 #[allow(non_snake_case)]
44 pub fn GLBX() -> Self {
45 *GLBX_LOCK.get_or_init(|| Self::from("GLBX"))
46 }
47 #[allow(non_snake_case)]
49 pub fn NYUM() -> Self {
50 *NYUM_LOCK.get_or_init(|| Self::from("NYUM"))
51 }
52 #[allow(non_snake_case)]
54 pub fn XCBT() -> Self {
55 *XCBT_LOCK.get_or_init(|| Self::from("XCBT"))
56 }
57 #[allow(non_snake_case)]
59 pub fn XCEC() -> Self {
60 *XCEC_LOCK.get_or_init(|| Self::from("XCEC"))
61 }
62 #[allow(non_snake_case)]
64 pub fn XCME() -> Self {
65 *XCME_LOCK.get_or_init(|| Self::from("XCME"))
66 }
67 #[allow(non_snake_case)]
69 pub fn XFXS() -> Self {
70 *XFXS_LOCK.get_or_init(|| Self::from("XFXS"))
71 }
72 #[allow(non_snake_case)]
74 pub fn XNYM() -> Self {
75 *XNYM_LOCK.get_or_init(|| Self::from("XNYM"))
76 }
77}
78
79pub static VENUE_MAP: LazyLock<Mutex<HashMap<&str, Venue>>> = LazyLock::new(|| {
80 let mut map = HashMap::new();
81 map.insert(Venue::CBCM().inner().as_str(), Venue::CBCM());
82 map.insert(Venue::GLBX().inner().as_str(), Venue::GLBX());
83 map.insert(Venue::NYUM().inner().as_str(), Venue::NYUM());
84 map.insert(Venue::XCBT().inner().as_str(), Venue::XCBT());
85 map.insert(Venue::XCEC().inner().as_str(), Venue::XCEC());
86 map.insert(Venue::XCME().inner().as_str(), Venue::XCME());
87 map.insert(Venue::XFXS().inner().as_str(), Venue::XFXS());
88 map.insert(Venue::XNYM().inner().as_str(), Venue::XNYM());
89 Mutex::new(map)
90});
91
92#[cfg(test)]
93mod tests {
94 use rstest::*;
95
96 use super::*;
97
98 #[rstest]
99 fn test_venue_constants() {
100 let cbcm1 = Venue::CBCM();
102 let cbcm2 = Venue::CBCM();
103 assert_eq!(cbcm1, cbcm2);
104 assert_eq!(cbcm1.inner().as_str(), "CBCM");
105
106 let glbx1 = Venue::GLBX();
107 let glbx2 = Venue::GLBX();
108 assert_eq!(glbx1, glbx2);
109 assert_eq!(glbx1.inner().as_str(), "GLBX");
110
111 let nyum1 = Venue::NYUM();
112 let nyum2 = Venue::NYUM();
113 assert_eq!(nyum1, nyum2);
114 assert_eq!(nyum1.inner().as_str(), "NYUM");
115
116 let xcbt1 = Venue::XCBT();
117 let xcbt2 = Venue::XCBT();
118 assert_eq!(xcbt1, xcbt2);
119 assert_eq!(xcbt1.inner().as_str(), "XCBT");
120
121 let xcec1 = Venue::XCEC();
122 let xcec2 = Venue::XCEC();
123 assert_eq!(xcec1, xcec2);
124 assert_eq!(xcec1.inner().as_str(), "XCEC");
125
126 let xcme1 = Venue::XCME();
127 let xcme2 = Venue::XCME();
128 assert_eq!(xcme1, xcme2);
129 assert_eq!(xcme1.inner().as_str(), "XCME");
130
131 let xfxs1 = Venue::XFXS();
132 let xfxs2 = Venue::XFXS();
133 assert_eq!(xfxs1, xfxs2);
134 assert_eq!(xfxs1.inner().as_str(), "XFXS");
135
136 let xnym1 = Venue::XNYM();
137 let xnym2 = Venue::XNYM();
138 assert_eq!(xnym1, xnym2);
139 assert_eq!(xnym1.inner().as_str(), "XNYM");
140 }
141
142 #[rstest]
143 fn test_venue_constants_uniqueness() {
144 let venues = [
146 Venue::CBCM(),
147 Venue::GLBX(),
148 Venue::NYUM(),
149 Venue::XCBT(),
150 Venue::XCEC(),
151 Venue::XCME(),
152 Venue::XFXS(),
153 Venue::XNYM(),
154 ];
155
156 for (i, venue1) in venues.iter().enumerate() {
158 for (j, venue2) in venues.iter().enumerate() {
159 if i != j {
160 assert_ne!(
161 venue1, venue2,
162 "Venues at indices {i} and {j} should be different"
163 );
164 }
165 }
166 }
167 }
168
169 #[rstest]
170 fn test_venue_map_contains_all_venues() {
171 let venue_map = VENUE_MAP.lock();
172
173 assert!(venue_map.contains_key("CBCM"));
175 assert!(venue_map.contains_key("GLBX"));
176 assert!(venue_map.contains_key("NYUM"));
177 assert!(venue_map.contains_key("XCBT"));
178 assert!(venue_map.contains_key("XCEC"));
179 assert!(venue_map.contains_key("XCME"));
180 assert!(venue_map.contains_key("XFXS"));
181 assert!(venue_map.contains_key("XNYM"));
182
183 assert_eq!(venue_map.len(), 8);
185 }
186
187 #[rstest]
188 fn test_venue_map_values_match_constants() {
189 let venue_map = VENUE_MAP.lock();
190
191 assert_eq!(venue_map.get("CBCM").unwrap(), &Venue::CBCM());
193 assert_eq!(venue_map.get("GLBX").unwrap(), &Venue::GLBX());
194 assert_eq!(venue_map.get("NYUM").unwrap(), &Venue::NYUM());
195 assert_eq!(venue_map.get("XCBT").unwrap(), &Venue::XCBT());
196 assert_eq!(venue_map.get("XCEC").unwrap(), &Venue::XCEC());
197 assert_eq!(venue_map.get("XCME").unwrap(), &Venue::XCME());
198 assert_eq!(venue_map.get("XFXS").unwrap(), &Venue::XFXS());
199 assert_eq!(venue_map.get("XNYM").unwrap(), &Venue::XNYM());
200 }
201
202 #[rstest]
203 fn test_venue_map_lookup_nonexistent() {
204 let venue_map = VENUE_MAP.lock();
205
206 assert!(venue_map.get("INVALID").is_none());
208 assert!(venue_map.get("").is_none());
209 assert!(venue_map.get("NYSE").is_none()); }
211
212 #[rstest]
213 fn test_venue_constants_lazy_initialization() {
214 let cbcm_calls = (0..10).map(|_| Venue::CBCM()).collect::<Vec<_>>();
219 let first_cbcm = cbcm_calls[0];
220
221 for cbcm in cbcm_calls {
222 assert_eq!(cbcm, first_cbcm);
223 }
224 }
225
226 #[rstest]
227 fn test_all_venue_strings() {
228 let expected_venues = vec![
230 ("CBCM", Venue::CBCM()),
231 ("GLBX", Venue::GLBX()),
232 ("NYUM", Venue::NYUM()),
233 ("XCBT", Venue::XCBT()),
234 ("XCEC", Venue::XCEC()),
235 ("XCME", Venue::XCME()),
236 ("XFXS", Venue::XFXS()),
237 ("XNYM", Venue::XNYM()),
238 ];
239
240 for (expected_str, venue) in expected_venues {
241 assert_eq!(venue.inner().as_str(), expected_str);
242 assert_eq!(format!("{venue}"), expected_str);
243 }
244 }
245
246 #[rstest]
247 #[expect(clippy::needless_collect)] fn test_venue_constants_thread_safety() {
249 use std::thread;
250
251 let handles: Vec<_> = (0..4)
253 .map(|_| {
254 thread::spawn(|| {
255 let venues = vec![
257 Venue::CBCM(),
258 Venue::GLBX(),
259 Venue::NYUM(),
260 Venue::XCBT(),
261 Venue::XCEC(),
262 Venue::XCME(),
263 Venue::XFXS(),
264 Venue::XNYM(),
265 ];
266 venues
267 })
268 })
269 .collect();
270
271 let results: Vec<Vec<Venue>> = handles.into_iter().map(|h| h.join().unwrap()).collect();
272
273 for venues in &results {
275 assert_eq!(venues[0], Venue::CBCM());
276 assert_eq!(venues[1], Venue::GLBX());
277 assert_eq!(venues[2], Venue::NYUM());
278 assert_eq!(venues[3], Venue::XCBT());
279 assert_eq!(venues[4], Venue::XCEC());
280 assert_eq!(venues[5], Venue::XCME());
281 assert_eq!(venues[6], Venue::XFXS());
282 assert_eq!(venues[7], Venue::XNYM());
283 }
284 }
285
286 #[rstest]
287 #[expect(clippy::needless_collect)] fn test_venue_map_thread_safety() {
289 use std::thread;
290
291 let handles: Vec<_> = (0..4)
293 .map(|_| {
294 thread::spawn(|| {
295 let venue_map = VENUE_MAP.lock();
296 venue_map.get("XCME").copied()
297 })
298 })
299 .collect();
300
301 let results: Vec<Option<Venue>> = handles.into_iter().map(|h| h.join().unwrap()).collect();
302
303 for result in results {
305 assert_eq!(result, Some(Venue::XCME()));
306 }
307 }
308}