1use std::{
19 collections::HashMap,
20 sync::{LazyLock, Mutex, OnceLock},
21};
22
23use crate::identifiers::Venue;
24
25static CBCM_LOCK: OnceLock<Venue> = OnceLock::new();
26static GLBX_LOCK: OnceLock<Venue> = OnceLock::new();
27static NYUM_LOCK: OnceLock<Venue> = OnceLock::new();
28static XCBT_LOCK: OnceLock<Venue> = OnceLock::new();
29static XCEC_LOCK: OnceLock<Venue> = OnceLock::new();
30static XCME_LOCK: OnceLock<Venue> = OnceLock::new();
31static XFXS_LOCK: OnceLock<Venue> = OnceLock::new();
32static XNYM_LOCK: OnceLock<Venue> = OnceLock::new();
33
34impl Venue {
35 #[allow(non_snake_case)]
37 pub fn CBCM() -> Self {
38 *CBCM_LOCK.get_or_init(|| Self::from("CBCM"))
39 }
40 #[allow(non_snake_case)]
42 pub fn GLBX() -> Self {
43 *GLBX_LOCK.get_or_init(|| Self::from("GLBX"))
44 }
45 #[allow(non_snake_case)]
47 pub fn NYUM() -> Self {
48 *NYUM_LOCK.get_or_init(|| Self::from("NYUM"))
49 }
50 #[allow(non_snake_case)]
52 pub fn XCBT() -> Self {
53 *XCBT_LOCK.get_or_init(|| Self::from("XCBT"))
54 }
55 #[allow(non_snake_case)]
57 pub fn XCEC() -> Self {
58 *XCEC_LOCK.get_or_init(|| Self::from("XCEC"))
59 }
60 #[allow(non_snake_case)]
62 pub fn XCME() -> Self {
63 *XCME_LOCK.get_or_init(|| Self::from("XCME"))
64 }
65 #[allow(non_snake_case)]
67 pub fn XFXS() -> Self {
68 *XFXS_LOCK.get_or_init(|| Self::from("XFXS"))
69 }
70 #[allow(non_snake_case)]
72 pub fn XNYM() -> Self {
73 *XNYM_LOCK.get_or_init(|| Self::from("XNYM"))
74 }
75}
76
77pub static VENUE_MAP: LazyLock<Mutex<HashMap<&str, Venue>>> = LazyLock::new(|| {
78 let mut map = HashMap::new();
79 map.insert(Venue::CBCM().inner().as_str(), Venue::CBCM());
80 map.insert(Venue::GLBX().inner().as_str(), Venue::GLBX());
81 map.insert(Venue::NYUM().inner().as_str(), Venue::NYUM());
82 map.insert(Venue::XCBT().inner().as_str(), Venue::XCBT());
83 map.insert(Venue::XCEC().inner().as_str(), Venue::XCEC());
84 map.insert(Venue::XCME().inner().as_str(), Venue::XCME());
85 map.insert(Venue::XFXS().inner().as_str(), Venue::XFXS());
86 map.insert(Venue::XNYM().inner().as_str(), Venue::XNYM());
87 Mutex::new(map)
88});
89
90#[cfg(test)]
94mod tests {
95 use rstest::*;
96
97 use super::*;
98
99 #[rstest]
100 fn test_venue_constants() {
101 let cbcm1 = Venue::CBCM();
103 let cbcm2 = Venue::CBCM();
104 assert_eq!(cbcm1, cbcm2);
105 assert_eq!(cbcm1.inner().as_str(), "CBCM");
106
107 let glbx1 = Venue::GLBX();
108 let glbx2 = Venue::GLBX();
109 assert_eq!(glbx1, glbx2);
110 assert_eq!(glbx1.inner().as_str(), "GLBX");
111
112 let nyum1 = Venue::NYUM();
113 let nyum2 = Venue::NYUM();
114 assert_eq!(nyum1, nyum2);
115 assert_eq!(nyum1.inner().as_str(), "NYUM");
116
117 let xcbt1 = Venue::XCBT();
118 let xcbt2 = Venue::XCBT();
119 assert_eq!(xcbt1, xcbt2);
120 assert_eq!(xcbt1.inner().as_str(), "XCBT");
121
122 let xcec1 = Venue::XCEC();
123 let xcec2 = Venue::XCEC();
124 assert_eq!(xcec1, xcec2);
125 assert_eq!(xcec1.inner().as_str(), "XCEC");
126
127 let xcme1 = Venue::XCME();
128 let xcme2 = Venue::XCME();
129 assert_eq!(xcme1, xcme2);
130 assert_eq!(xcme1.inner().as_str(), "XCME");
131
132 let xfxs1 = Venue::XFXS();
133 let xfxs2 = Venue::XFXS();
134 assert_eq!(xfxs1, xfxs2);
135 assert_eq!(xfxs1.inner().as_str(), "XFXS");
136
137 let xnym1 = Venue::XNYM();
138 let xnym2 = Venue::XNYM();
139 assert_eq!(xnym1, xnym2);
140 assert_eq!(xnym1.inner().as_str(), "XNYM");
141 }
142
143 #[rstest]
144 fn test_venue_constants_uniqueness() {
145 let venues = [
147 Venue::CBCM(),
148 Venue::GLBX(),
149 Venue::NYUM(),
150 Venue::XCBT(),
151 Venue::XCEC(),
152 Venue::XCME(),
153 Venue::XFXS(),
154 Venue::XNYM(),
155 ];
156
157 for (i, venue1) in venues.iter().enumerate() {
159 for (j, venue2) in venues.iter().enumerate() {
160 if i != j {
161 assert_ne!(
162 venue1, venue2,
163 "Venues at indices {i} and {j} should be different"
164 );
165 }
166 }
167 }
168 }
169
170 #[rstest]
171 fn test_venue_map_contains_all_venues() {
172 let venue_map = VENUE_MAP.lock().unwrap();
173
174 assert!(venue_map.contains_key("CBCM"));
176 assert!(venue_map.contains_key("GLBX"));
177 assert!(venue_map.contains_key("NYUM"));
178 assert!(venue_map.contains_key("XCBT"));
179 assert!(venue_map.contains_key("XCEC"));
180 assert!(venue_map.contains_key("XCME"));
181 assert!(venue_map.contains_key("XFXS"));
182 assert!(venue_map.contains_key("XNYM"));
183
184 assert_eq!(venue_map.len(), 8);
186 }
187
188 #[rstest]
189 fn test_venue_map_values_match_constants() {
190 let venue_map = VENUE_MAP.lock().unwrap();
191
192 assert_eq!(venue_map.get("CBCM").unwrap(), &Venue::CBCM());
194 assert_eq!(venue_map.get("GLBX").unwrap(), &Venue::GLBX());
195 assert_eq!(venue_map.get("NYUM").unwrap(), &Venue::NYUM());
196 assert_eq!(venue_map.get("XCBT").unwrap(), &Venue::XCBT());
197 assert_eq!(venue_map.get("XCEC").unwrap(), &Venue::XCEC());
198 assert_eq!(venue_map.get("XCME").unwrap(), &Venue::XCME());
199 assert_eq!(venue_map.get("XFXS").unwrap(), &Venue::XFXS());
200 assert_eq!(venue_map.get("XNYM").unwrap(), &Venue::XNYM());
201 }
202
203 #[rstest]
204 fn test_venue_map_lookup_nonexistent() {
205 let venue_map = VENUE_MAP.lock().unwrap();
206
207 assert!(venue_map.get("INVALID").is_none());
209 assert!(venue_map.get("").is_none());
210 assert!(venue_map.get("NYSE").is_none()); }
212
213 #[rstest]
214 fn test_venue_constants_lazy_initialization() {
215 let cbcm_calls = (0..10).map(|_| Venue::CBCM()).collect::<Vec<_>>();
220 let first_cbcm = cbcm_calls[0];
221
222 for cbcm in cbcm_calls {
223 assert_eq!(cbcm, first_cbcm);
224 }
225 }
226
227 #[rstest]
228 fn test_all_venue_strings() {
229 let expected_venues = vec![
231 ("CBCM", Venue::CBCM()),
232 ("GLBX", Venue::GLBX()),
233 ("NYUM", Venue::NYUM()),
234 ("XCBT", Venue::XCBT()),
235 ("XCEC", Venue::XCEC()),
236 ("XCME", Venue::XCME()),
237 ("XFXS", Venue::XFXS()),
238 ("XNYM", Venue::XNYM()),
239 ];
240
241 for (expected_str, venue) in expected_venues {
242 assert_eq!(venue.inner().as_str(), expected_str);
243 assert_eq!(format!("{venue}"), expected_str);
244 }
245 }
246
247 #[rstest]
248 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 fn test_venue_map_thread_safety() {
288 use std::thread;
289
290 let handles: Vec<_> = (0..4)
292 .map(|_| {
293 thread::spawn(|| {
294 let venue_map = VENUE_MAP.lock().unwrap();
295 venue_map.get("XCME").copied()
296 })
297 })
298 .collect();
299
300 let results: Vec<Option<Venue>> = handles.into_iter().map(|h| h.join().unwrap()).collect();
301
302 for result in results {
304 assert_eq!(result, Some(Venue::XCME()));
305 }
306 }
307}