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