1use super::swift_utils::parse_swift_digits;
2use crate::errors::ParseError;
3use crate::traits::SwiftField;
4use chrono::{Datelike, NaiveDate};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18pub struct Field11R {
19 pub message_type: String,
21
22 #[serde(with = "date_string")]
24 pub date: NaiveDate,
25
26 pub session_number: Option<String>,
28
29 pub input_sequence_number: Option<String>,
31}
32
33impl SwiftField for Field11R {
34 fn parse(input: &str) -> crate::Result<Self>
35 where
36 Self: Sized,
37 {
38 let mut remaining = input;
39
40 if remaining.len() < 3 {
42 return Err(ParseError::InvalidFormat {
43 message: "Field 11R message type requires exactly 3 digits".to_string(),
44 });
45 }
46 let message_type = parse_swift_digits(&remaining[..3], "Field 11R message type")?;
47 remaining = &remaining[3..];
48
49 if remaining.len() < 6 {
51 return Err(ParseError::InvalidFormat {
52 message: "Field 11R date requires exactly 6 digits".to_string(),
53 });
54 }
55 let date_str = parse_swift_digits(&remaining[..6], "Field 11R date")?;
56 remaining = &remaining[6..];
57
58 let year = 2000
60 + date_str[0..2]
61 .parse::<i32>()
62 .map_err(|_| ParseError::InvalidFormat {
63 message: "Invalid year in Field 11R".to_string(),
64 })?;
65 let month = date_str[2..4]
66 .parse::<u32>()
67 .map_err(|_| ParseError::InvalidFormat {
68 message: "Invalid month in Field 11R".to_string(),
69 })?;
70 let day = date_str[4..6]
71 .parse::<u32>()
72 .map_err(|_| ParseError::InvalidFormat {
73 message: "Invalid day in Field 11R".to_string(),
74 })?;
75
76 let date =
77 NaiveDate::from_ymd_opt(year, month, day).ok_or_else(|| ParseError::InvalidFormat {
78 message: format!("Invalid date in Field 11R: {}", date_str),
79 })?;
80
81 let session_number =
83 if remaining.len() >= 4 && remaining[..4].chars().all(|c| c.is_ascii_digit()) {
84 let session = Some(remaining[..4].to_string());
85 remaining = &remaining[4..];
86 session
87 } else {
88 None
89 };
90
91 let input_sequence_number =
93 if remaining.len() >= 6 && remaining[..6].chars().all(|c| c.is_ascii_digit()) {
94 Some(remaining[..6].to_string())
95 } else {
96 None
97 };
98
99 Ok(Field11R {
100 message_type,
101 date,
102 session_number,
103 input_sequence_number,
104 })
105 }
106
107 fn to_swift_string(&self) -> String {
108 let date_str = format!(
109 "{:02}{:02}{:02}",
110 self.date.year() % 100,
111 self.date.month(),
112 self.date.day()
113 );
114
115 let mut result = format!(":11R:{}{}", self.message_type, date_str);
116
117 if let Some(ref session) = self.session_number {
118 result.push_str(session);
119 }
120
121 if let Some(ref seq) = self.input_sequence_number {
122 result.push_str(seq);
123 }
124
125 result
126 }
127}
128
129#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
140pub struct Field11S {
141 pub message_type: String,
143
144 #[serde(with = "date_string")]
146 pub date: NaiveDate,
147
148 pub session_number: Option<String>,
150
151 pub input_sequence_number: Option<String>,
153}
154
155mod date_string {
157 use chrono::NaiveDate;
158 use serde::{Deserialize, Deserializer, Serializer};
159
160 pub fn serialize<S>(date: &NaiveDate, serializer: S) -> Result<S::Ok, S::Error>
161 where
162 S: Serializer,
163 {
164 serializer.serialize_str(&date.format("%Y-%m-%d").to_string())
165 }
166
167 pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveDate, D::Error>
168 where
169 D: Deserializer<'de>,
170 {
171 let s = String::deserialize(deserializer)?;
172 NaiveDate::parse_from_str(&s, "%Y-%m-%d").map_err(serde::de::Error::custom)
173 }
174}
175
176impl SwiftField for Field11S {
177 fn parse(input: &str) -> crate::Result<Self>
178 where
179 Self: Sized,
180 {
181 let mut remaining = input;
182
183 if remaining.len() < 3 {
185 return Err(ParseError::InvalidFormat {
186 message: "Field 11S message type requires exactly 3 digits".to_string(),
187 });
188 }
189 let message_type = parse_swift_digits(&remaining[..3], "Field 11S message type")?;
190 remaining = &remaining[3..];
191
192 if remaining.len() < 6 {
194 return Err(ParseError::InvalidFormat {
195 message: "Field 11S date requires exactly 6 digits".to_string(),
196 });
197 }
198 let date_str = parse_swift_digits(&remaining[..6], "Field 11S date")?;
199 remaining = &remaining[6..];
200
201 let year = 2000
203 + date_str[0..2]
204 .parse::<i32>()
205 .map_err(|_| ParseError::InvalidFormat {
206 message: "Invalid year in Field 11S".to_string(),
207 })?;
208 let month = date_str[2..4]
209 .parse::<u32>()
210 .map_err(|_| ParseError::InvalidFormat {
211 message: "Invalid month in Field 11S".to_string(),
212 })?;
213 let day = date_str[4..6]
214 .parse::<u32>()
215 .map_err(|_| ParseError::InvalidFormat {
216 message: "Invalid day in Field 11S".to_string(),
217 })?;
218
219 let date =
220 NaiveDate::from_ymd_opt(year, month, day).ok_or_else(|| ParseError::InvalidFormat {
221 message: format!("Invalid date in Field 11S: {}", date_str),
222 })?;
223
224 let session_number =
226 if remaining.len() >= 4 && remaining[..4].chars().all(|c| c.is_ascii_digit()) {
227 let session = Some(remaining[..4].to_string());
228 remaining = &remaining[4..];
229 session
230 } else {
231 None
232 };
233
234 let input_sequence_number =
236 if remaining.len() >= 6 && remaining[..6].chars().all(|c| c.is_ascii_digit()) {
237 Some(remaining[..6].to_string())
238 } else {
239 None
240 };
241
242 Ok(Field11S {
243 message_type,
244 date,
245 session_number,
246 input_sequence_number,
247 })
248 }
249
250 fn to_swift_string(&self) -> String {
251 let date_str = format!(
252 "{:02}{:02}{:02}",
253 self.date.year() % 100,
254 self.date.month(),
255 self.date.day()
256 );
257
258 let mut result = format!(":11S:{}{}", self.message_type, date_str);
259
260 if let Some(ref session) = self.session_number {
261 result.push_str(session);
262 }
263
264 if let Some(ref seq) = self.input_sequence_number {
265 result.push_str(seq);
266 }
267
268 result
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275
276 #[test]
277 fn test_field11r_parse() {
278 let field = Field11R::parse("1032407191234567890").unwrap();
280 assert_eq!(field.message_type, "103");
281 assert_eq!(field.date.year(), 2024);
282 assert_eq!(field.date.month(), 7);
283 assert_eq!(field.date.day(), 19);
284 assert_eq!(field.session_number, Some("1234".to_string()));
285 assert_eq!(field.input_sequence_number, Some("567890".to_string()));
286
287 let field = Field11R::parse("202240315").unwrap();
289 assert_eq!(field.message_type, "202");
290 assert_eq!(field.date.year(), 2024);
291 assert_eq!(field.date.month(), 3);
292 assert_eq!(field.date.day(), 15);
293 assert_eq!(field.session_number, None);
294 assert_eq!(field.input_sequence_number, None);
295
296 let field = Field11R::parse("9402407191234").unwrap();
298 assert_eq!(field.message_type, "940");
299 assert_eq!(field.session_number, Some("1234".to_string()));
300 assert_eq!(field.input_sequence_number, None);
301 }
302
303 #[test]
304 fn test_field11s_parse() {
305 let field = Field11S::parse("1922407191234567890").unwrap();
307 assert_eq!(field.message_type, "192");
308 assert_eq!(field.date.year(), 2024);
309 assert_eq!(field.date.month(), 7);
310 assert_eq!(field.date.day(), 19);
311 assert_eq!(field.session_number, Some("1234".to_string()));
312 assert_eq!(field.input_sequence_number, Some("567890".to_string()));
313
314 let field = Field11S::parse("292240315").unwrap();
316 assert_eq!(field.message_type, "292");
317 assert_eq!(field.date.year(), 2024);
318 assert_eq!(field.date.month(), 3);
319 assert_eq!(field.date.day(), 15);
320 assert_eq!(field.session_number, None);
321 assert_eq!(field.input_sequence_number, None);
322 }
323
324 #[test]
325 fn test_field11r_to_swift_string() {
326 let field = Field11R {
327 message_type: "103".to_string(),
328 date: NaiveDate::from_ymd_opt(2024, 7, 19).unwrap(),
329 session_number: Some("1234".to_string()),
330 input_sequence_number: Some("567890".to_string()),
331 };
332 assert_eq!(field.to_swift_string(), ":11R:1032407191234567890");
333
334 let field = Field11R {
335 message_type: "202".to_string(),
336 date: NaiveDate::from_ymd_opt(2024, 3, 15).unwrap(),
337 session_number: None,
338 input_sequence_number: None,
339 };
340 assert_eq!(field.to_swift_string(), ":11R:202240315");
341 }
342
343 #[test]
344 fn test_field11s_to_swift_string() {
345 let field = Field11S {
346 message_type: "192".to_string(),
347 date: NaiveDate::from_ymd_opt(2024, 7, 19).unwrap(),
348 session_number: Some("1234".to_string()),
349 input_sequence_number: Some("567890".to_string()),
350 };
351 assert_eq!(field.to_swift_string(), ":11S:1922407191234567890");
352
353 let field = Field11S {
354 message_type: "292".to_string(),
355 date: NaiveDate::from_ymd_opt(2024, 3, 15).unwrap(),
356 session_number: None,
357 input_sequence_number: None,
358 };
359 assert_eq!(field.to_swift_string(), ":11S:292240315");
360 }
361}
362
363#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
374pub struct Field11 {
375 pub message_type: String,
377
378 pub date: NaiveDate,
380}
381
382impl SwiftField for Field11 {
383 fn parse(input: &str) -> crate::Result<Self>
384 where
385 Self: Sized,
386 {
387 if input.len() < 9 {
389 return Err(ParseError::InvalidFormat {
390 message: "Field 11 requires at least 9 characters (3 for MT + 6 for date)"
391 .to_string(),
392 });
393 }
394
395 let message_type = parse_swift_digits(&input[..3], "Field 11 message type")?;
397
398 let date_str = parse_swift_digits(&input[3..9], "Field 11 date")?;
400
401 let year = 2000
403 + date_str[0..2]
404 .parse::<i32>()
405 .map_err(|_| ParseError::InvalidFormat {
406 message: "Invalid year in Field 11".to_string(),
407 })?;
408 let month = date_str[2..4]
409 .parse::<u32>()
410 .map_err(|_| ParseError::InvalidFormat {
411 message: "Invalid month in Field 11".to_string(),
412 })?;
413 let day = date_str[4..6]
414 .parse::<u32>()
415 .map_err(|_| ParseError::InvalidFormat {
416 message: "Invalid day in Field 11".to_string(),
417 })?;
418
419 let date =
420 NaiveDate::from_ymd_opt(year, month, day).ok_or_else(|| ParseError::InvalidFormat {
421 message: format!("Invalid date in Field 11: {}", date_str),
422 })?;
423
424 Ok(Field11 { message_type, date })
425 }
426
427 fn to_swift_string(&self) -> String {
428 let date_str = format!(
429 "{:02}{:02}{:02}",
430 self.date.year() % 100,
431 self.date.month(),
432 self.date.day()
433 );
434 format!(":11:{}{}", self.message_type, date_str)
435 }
436}
437
438#[cfg(test)]
439mod field11_tests {
440 use super::*;
441
442 #[test]
443 fn test_field11_parse() {
444 let field = Field11::parse("192240719").unwrap();
445 assert_eq!(field.message_type, "192");
446 assert_eq!(field.date.year(), 2024);
447 assert_eq!(field.date.month(), 7);
448 assert_eq!(field.date.day(), 19);
449 }
450
451 #[test]
452 fn test_field11_to_swift_string() {
453 let field = Field11 {
454 message_type: "196".to_string(),
455 date: NaiveDate::from_ymd_opt(2024, 7, 19).unwrap(),
456 };
457 assert_eq!(field.to_swift_string(), ":11:196240719");
458 }
459}