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)]
91mod tests {
92 use nautilus_core::MUTEX_POISONED;
93 use rstest::*;
94
95 use super::*;
96
97 #[rstest]
98 fn test_venue_constants() {
99 let cbcm1 = Venue::CBCM();
101 let cbcm2 = Venue::CBCM();
102 assert_eq!(cbcm1, cbcm2);
103 assert_eq!(cbcm1.inner().as_str(), "CBCM");
104
105 let glbx1 = Venue::GLBX();
106 let glbx2 = Venue::GLBX();
107 assert_eq!(glbx1, glbx2);
108 assert_eq!(glbx1.inner().as_str(), "GLBX");
109
110 let nyum1 = Venue::NYUM();
111 let nyum2 = Venue::NYUM();
112 assert_eq!(nyum1, nyum2);
113 assert_eq!(nyum1.inner().as_str(), "NYUM");
114
115 let xcbt1 = Venue::XCBT();
116 let xcbt2 = Venue::XCBT();
117 assert_eq!(xcbt1, xcbt2);
118 assert_eq!(xcbt1.inner().as_str(), "XCBT");
119
120 let xcec1 = Venue::XCEC();
121 let xcec2 = Venue::XCEC();
122 assert_eq!(xcec1, xcec2);
123 assert_eq!(xcec1.inner().as_str(), "XCEC");
124
125 let xcme1 = Venue::XCME();
126 let xcme2 = Venue::XCME();
127 assert_eq!(xcme1, xcme2);
128 assert_eq!(xcme1.inner().as_str(), "XCME");
129
130 let xfxs1 = Venue::XFXS();
131 let xfxs2 = Venue::XFXS();
132 assert_eq!(xfxs1, xfxs2);
133 assert_eq!(xfxs1.inner().as_str(), "XFXS");
134
135 let xnym1 = Venue::XNYM();
136 let xnym2 = Venue::XNYM();
137 assert_eq!(xnym1, xnym2);
138 assert_eq!(xnym1.inner().as_str(), "XNYM");
139 }
140
141 #[rstest]
142 fn test_venue_constants_uniqueness() {
143 let venues = [
145 Venue::CBCM(),
146 Venue::GLBX(),
147 Venue::NYUM(),
148 Venue::XCBT(),
149 Venue::XCEC(),
150 Venue::XCME(),
151 Venue::XFXS(),
152 Venue::XNYM(),
153 ];
154
155 for (i, venue1) in venues.iter().enumerate() {
157 for (j, venue2) in venues.iter().enumerate() {
158 if i != j {
159 assert_ne!(
160 venue1, venue2,
161 "Venues at indices {i} and {j} should be different"
162 );
163 }
164 }
165 }
166 }
167
168 #[rstest]
169 fn test_venue_map_contains_all_venues() {
170 let venue_map = VENUE_MAP.lock().expect(MUTEX_POISONED);
171
172 assert!(venue_map.contains_key("CBCM"));
174 assert!(venue_map.contains_key("GLBX"));
175 assert!(venue_map.contains_key("NYUM"));
176 assert!(venue_map.contains_key("XCBT"));
177 assert!(venue_map.contains_key("XCEC"));
178 assert!(venue_map.contains_key("XCME"));
179 assert!(venue_map.contains_key("XFXS"));
180 assert!(venue_map.contains_key("XNYM"));
181
182 assert_eq!(venue_map.len(), 8);
184 }
185
186 #[rstest]
187 fn test_venue_map_values_match_constants() {
188 let venue_map = VENUE_MAP.lock().expect(MUTEX_POISONED);
189
190 assert_eq!(venue_map.get("CBCM").unwrap(), &Venue::CBCM());
192 assert_eq!(venue_map.get("GLBX").unwrap(), &Venue::GLBX());
193 assert_eq!(venue_map.get("NYUM").unwrap(), &Venue::NYUM());
194 assert_eq!(venue_map.get("XCBT").unwrap(), &Venue::XCBT());
195 assert_eq!(venue_map.get("XCEC").unwrap(), &Venue::XCEC());
196 assert_eq!(venue_map.get("XCME").unwrap(), &Venue::XCME());
197 assert_eq!(venue_map.get("XFXS").unwrap(), &Venue::XFXS());
198 assert_eq!(venue_map.get("XNYM").unwrap(), &Venue::XNYM());
199 }
200
201 #[rstest]
202 fn test_venue_map_lookup_nonexistent() {
203 let venue_map = VENUE_MAP.lock().expect(MUTEX_POISONED);
204
205 assert!(venue_map.get("INVALID").is_none());
207 assert!(venue_map.get("").is_none());
208 assert!(venue_map.get("NYSE").is_none()); }
210
211 #[rstest]
212 fn test_venue_constants_lazy_initialization() {
213 let cbcm_calls = (0..10).map(|_| Venue::CBCM()).collect::<Vec<_>>();
218 let first_cbcm = cbcm_calls[0];
219
220 for cbcm in cbcm_calls {
221 assert_eq!(cbcm, first_cbcm);
222 }
223 }
224
225 #[rstest]
226 fn test_all_venue_strings() {
227 let expected_venues = vec![
229 ("CBCM", Venue::CBCM()),
230 ("GLBX", Venue::GLBX()),
231 ("NYUM", Venue::NYUM()),
232 ("XCBT", Venue::XCBT()),
233 ("XCEC", Venue::XCEC()),
234 ("XCME", Venue::XCME()),
235 ("XFXS", Venue::XFXS()),
236 ("XNYM", Venue::XNYM()),
237 ];
238
239 for (expected_str, venue) in expected_venues {
240 assert_eq!(venue.inner().as_str(), expected_str);
241 assert_eq!(format!("{venue}"), expected_str);
242 }
243 }
244
245 #[rstest]
246 #[allow(clippy::needless_collect)] fn test_venue_constants_thread_safety() {
248 use std::thread;
249
250 let handles: Vec<_> = (0..4)
252 .map(|_| {
253 thread::spawn(|| {
254 let venues = vec![
256 Venue::CBCM(),
257 Venue::GLBX(),
258 Venue::NYUM(),
259 Venue::XCBT(),
260 Venue::XCEC(),
261 Venue::XCME(),
262 Venue::XFXS(),
263 Venue::XNYM(),
264 ];
265 venues
266 })
267 })
268 .collect();
269
270 let results: Vec<Vec<Venue>> = handles.into_iter().map(|h| h.join().unwrap()).collect();
271
272 for venues in &results {
274 assert_eq!(venues[0], Venue::CBCM());
275 assert_eq!(venues[1], Venue::GLBX());
276 assert_eq!(venues[2], Venue::NYUM());
277 assert_eq!(venues[3], Venue::XCBT());
278 assert_eq!(venues[4], Venue::XCEC());
279 assert_eq!(venues[5], Venue::XCME());
280 assert_eq!(venues[6], Venue::XFXS());
281 assert_eq!(venues[7], Venue::XNYM());
282 }
283 }
284
285 #[rstest]
286 #[allow(clippy::needless_collect)] 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().expect(MUTEX_POISONED);
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}