1use std::str;
8
9use serde::{Deserialize, Serialize};
10
11mod common;
12
13#[cfg(test)]
15use common::decoders;
16use common::encoders;
17
18#[cfg(feature = "sync")]
20mod sync;
21
22#[cfg(feature = "async")]
23mod r#async;
24
25#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
27pub struct WshMetadata {
28 pub data_json: String,
30}
31
32#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
34pub struct WshEventData {
35 pub data_json: String,
37}
38
39#[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)]
45pub struct AutoFill {
46 pub competitors: bool,
48 pub portfolio: bool,
50 pub watchlist: bool,
52}
53
54impl AutoFill {
55 pub fn is_specified(&self) -> bool {
57 self.competitors || self.portfolio || self.watchlist
58 }
59}
60
61#[cfg(feature = "sync")]
63pub mod blocking {
65 pub(crate) use super::sync::{wsh_event_data_by_contract, wsh_event_data_by_filter, wsh_metadata};
66}
67
68#[cfg(feature = "async")]
69pub(crate) use r#async::{wsh_event_data_by_contract, wsh_event_data_by_filter, wsh_metadata};
70
71#[cfg(test)]
73mod common_tests {
74 use super::*;
75 use crate::messages::ResponseMessage;
76
77 #[test]
78 fn test_autofill_is_specified() {
79 assert!(!AutoFill::default().is_specified());
80
81 assert!(AutoFill {
82 competitors: true,
83 portfolio: false,
84 watchlist: false,
85 }
86 .is_specified());
87
88 assert!(AutoFill {
89 competitors: false,
90 portfolio: true,
91 watchlist: false,
92 }
93 .is_specified());
94
95 assert!(AutoFill {
96 competitors: false,
97 portfolio: false,
98 watchlist: true,
99 }
100 .is_specified());
101 }
102
103 #[test]
104 fn test_autofill_combinations() {
105 let combinations = vec![
107 (false, false, false, false),
108 (true, false, false, true),
109 (false, true, false, true),
110 (false, false, true, true),
111 (true, true, false, true),
112 (true, false, true, true),
113 (false, true, true, true),
114 (true, true, true, true),
115 ];
116
117 for (competitors, portfolio, watchlist, expected) in combinations {
118 let autofill = AutoFill {
119 competitors,
120 portfolio,
121 watchlist,
122 };
123 assert_eq!(
124 autofill.is_specified(),
125 expected,
126 "Failed for combination: competitors={}, portfolio={}, watchlist={}",
127 competitors,
128 portfolio,
129 watchlist
130 );
131 }
132 }
133
134 #[test]
135 fn test_decode_wsh_metadata() {
136 use super::decoders::decode_wsh_metadata;
137
138 let message = ResponseMessage::from("104\09000\0{\"test\":\"data\"}\0");
139 let result = decode_wsh_metadata(message);
140
141 assert!(result.is_ok(), "failed to decode wsh metadata: {}", result.err().unwrap());
142 assert_eq!(result.unwrap().data_json, "{\"test\":\"data\"}");
143 }
144
145 #[test]
146 fn test_decode_wsh_event_data() {
147 use super::decoders::decode_wsh_event_data;
148
149 let message = ResponseMessage::from("105\09000\0{\"test\":\"data\"}\0");
150 let result = decode_wsh_event_data(message);
151
152 assert!(result.is_ok(), "failed to decode wsh event data: {}", result.err().unwrap());
153 assert_eq!(result.unwrap().data_json, "{\"test\":\"data\"}");
154 }
155
156 #[test]
157 fn test_encode_request_wsh_metadata() {
158 use super::encoders::encode_request_wsh_metadata;
159
160 let result = encode_request_wsh_metadata(9000);
161 assert!(result.is_ok());
162 assert_eq!(result.unwrap().encode_simple(), "100|9000|");
163 }
164
165 #[test]
166 fn test_encode_cancel_wsh_metadata() {
167 use super::encoders::encode_cancel_wsh_metadata;
168
169 let result = encode_cancel_wsh_metadata(9000);
170 assert!(result.is_ok());
171 assert_eq!(result.unwrap().encode_simple(), "101|9000|");
172 }
173
174 #[test]
175 fn test_encode_request_wsh_event_data() {
176 use super::encoders::encode_request_wsh_event_data;
177 use crate::server_versions;
178 use time::macros::date;
179
180 let result = encode_request_wsh_event_data(server_versions::WSHE_CALENDAR, 9000, Some(12345), None, None, None, None, None);
182 assert!(result.is_ok());
183 assert_eq!(result.unwrap().encode_simple(), "102|9000|12345|");
184
185 let result = encode_request_wsh_event_data(
187 server_versions::WSH_EVENT_DATA_FILTERS_DATE,
188 9000,
189 Some(12345),
190 Some("filter"),
191 Some(date!(2024 - 01 - 01)),
192 Some(date!(2024 - 12 - 31)),
193 Some(100),
194 Some(AutoFill {
195 competitors: true,
196 portfolio: false,
197 watchlist: true,
198 }),
199 );
200 assert!(result.is_ok());
201 assert_eq!(result.unwrap().encode_simple(), "102|9000|12345|filter|1|0|1|20240101|20241231|100|");
202 }
203
204 #[test]
205 fn test_encode_cancel_wsh_event_data() {
206 use super::encoders::encode_cancel_wsh_event_data;
207
208 let result = encode_cancel_wsh_event_data(9000);
209 assert!(result.is_ok());
210 assert_eq!(result.unwrap().encode_simple(), "103|9000|");
211 }
212
213 #[test]
214 fn test_decode_wsh_metadata_empty_json() {
215 use super::decoders::decode_wsh_metadata;
216
217 let message = ResponseMessage::from("104\09000\0\0");
218 let result = decode_wsh_metadata(message);
219
220 assert!(result.is_ok(), "failed to decode empty wsh metadata: {}", result.err().unwrap());
221 assert_eq!(result.unwrap().data_json, "");
222 }
223
224 #[test]
225 fn test_decode_wsh_event_data_empty_json() {
226 use super::decoders::decode_wsh_event_data;
227
228 let message = ResponseMessage::from("105\09000\0\0");
229 let result = decode_wsh_event_data(message);
230
231 assert!(result.is_ok(), "failed to decode empty wsh event data: {}", result.err().unwrap());
232 assert_eq!(result.unwrap().data_json, "");
233 }
234
235 #[test]
236 fn test_decode_wsh_metadata_with_special_chars() {
237 use super::decoders::decode_wsh_metadata;
238
239 let message = ResponseMessage::from("104\09000\0{\"data\":\"test\\nwith\\tspecial\\rchars\"}\0");
240 let result = decode_wsh_metadata(message);
241
242 assert!(result.is_ok());
243 assert_eq!(result.unwrap().data_json, "{\"data\":\"test\\nwith\\tspecial\\rchars\"}");
244 }
245
246 #[test]
247 fn test_encode_request_wsh_event_data_edge_cases() {
248 use super::encoders::encode_request_wsh_event_data;
249 use crate::server_versions;
250
251 let result = encode_request_wsh_event_data(server_versions::WSH_EVENT_DATA_FILTERS, 9000, None, Some(""), None, None, None, None);
253 assert!(result.is_ok());
254 assert_eq!(result.unwrap().encode_simple(), "102|9000|||0|0|0|");
255
256 let result = encode_request_wsh_event_data(
258 server_versions::WSH_EVENT_DATA_FILTERS,
259 9001,
260 None,
261 Some("filter=\"test\" AND type='earnings'"),
262 None,
263 None,
264 None,
265 None,
266 );
267 assert!(result.is_ok());
268 assert_eq!(result.unwrap().encode_simple(), "102|9001||filter=\"test\" AND type='earnings'|0|0|0|");
269
270 let result = encode_request_wsh_event_data(
272 server_versions::WSH_EVENT_DATA_FILTERS_DATE,
273 9002,
274 Some(12345),
275 None,
276 None,
277 None,
278 Some(-10),
279 None,
280 );
281 assert!(result.is_ok());
282 assert_eq!(result.unwrap().encode_simple(), "102|9002|12345||0|0|0|||-10|");
283 }
284}