1use std::collections::{HashMap, HashSet};
46
47use crate::errors::{ParseError, ParserConfig, Result, SwiftValidationError};
48use crate::headers::{ApplicationHeader, BasicHeader, Trailer, UserHeader};
49use crate::messages::{
50 MT101, MT103, MT104, MT107, MT110, MT111, MT112, MT192, MT196, MT199, MT202, MT205, MT210,
51 MT292, MT296, MT299, MT900, MT910, MT920, MT935, MT940, MT941, MT942, MT950,
52};
53use crate::swift_error_codes::t_series;
54use crate::{ParsedSwiftMessage, SwiftMessage, SwiftMessageBody};
55
56type FieldParseResult = Result<HashMap<String, Vec<(String, usize)>>>;
61
62#[derive(Debug, Clone)]
64pub struct ParsingContext {
65 pub current_field: Option<String>,
67 pub current_component: Option<String>,
69 pub message_type: String,
71 pub original_message: String,
73}
74
75impl ParsingContext {
76 pub fn new(message_type: String, original_message: String) -> Self {
78 Self {
79 current_field: None,
80 current_component: None,
81 message_type,
82 original_message,
83 }
84 }
85
86 pub fn with_field(&self, field: String) -> Self {
88 let mut ctx = self.clone();
89 ctx.current_field = Some(field);
90 ctx.current_component = None;
91 ctx
92 }
93
94 pub fn with_component(&self, component: String) -> Self {
96 let mut ctx = self.clone();
97 ctx.current_component = Some(component);
98 ctx
99 }
100}
101
102#[derive(Debug, Clone)]
131pub struct FieldConsumptionTracker {
132 consumed_indices: HashMap<String, HashSet<usize>>,
134}
135
136impl Default for FieldConsumptionTracker {
137 fn default() -> Self {
138 Self::new()
139 }
140}
141
142impl FieldConsumptionTracker {
143 pub fn new() -> Self {
145 Self {
146 consumed_indices: HashMap::new(),
147 }
148 }
149
150 pub fn mark_consumed(&mut self, tag: &str, index: usize) {
152 use std::collections::hash_map::Entry;
154 match self.consumed_indices.entry(tag.to_string()) {
155 Entry::Occupied(mut e) => {
156 e.get_mut().insert(index);
157 }
158 Entry::Vacant(e) => {
159 let mut set = HashSet::new();
160 set.insert(index);
161 e.insert(set);
162 }
163 }
164 }
165
166 pub fn get_next_available<'a>(
168 &self,
169 tag: &str,
170 values: &'a [(String, usize)],
171 ) -> Option<(&'a str, usize)> {
172 let consumed_set = self.consumed_indices.get(tag);
173
174 values
176 .iter()
177 .find(|(_, pos)| consumed_set.is_none_or(|set| !set.contains(pos)))
178 .map(|(value, pos)| (value.as_str(), *pos))
179 }
180}
181
182pub fn find_field_with_variant_sequential_constrained(
201 fields: &HashMap<String, Vec<(String, usize)>>,
202 base_tag: &str,
203 tracker: &mut FieldConsumptionTracker,
204 valid_variants: Option<&[&str]>,
205) -> Option<(String, Option<String>, usize)> {
206 if let Some(values) = fields.get(base_tag) {
208 if let Some((value, pos)) = tracker.get_next_available(base_tag, values) {
209 tracker.mark_consumed(base_tag, pos);
210 return Some((value.to_string(), None, pos));
211 }
212 }
213
214 let mut variant_candidates: Vec<(&String, &Vec<(String, usize)>)> = fields
217 .iter()
218 .filter(|(tag, _)| {
219 tag.starts_with(base_tag)
220 && tag.len() == base_tag.len() + 1
221 && tag
222 .chars()
223 .last()
224 .is_some_and(|c| c.is_ascii_alphabetic() && c.is_ascii_uppercase())
225 })
226 .collect();
227
228 variant_candidates.sort_by_key(|(tag, values)| {
230 values
231 .iter()
232 .filter(|(_, pos)| {
233 tracker
234 .consumed_indices
235 .get(*tag)
236 .is_none_or(|set| !set.contains(pos))
237 })
238 .map(|(_, pos)| *pos)
239 .min()
240 .unwrap_or(usize::MAX)
241 });
242
243 for (tag, values) in variant_candidates {
244 let variant_char = tag.chars().last().unwrap();
245 let variant_str = variant_char.to_string();
246
247 if let Some(valid) = valid_variants {
249 if !valid.contains(&variant_str.as_str()) {
250 continue; }
252 }
253
254 if let Some((value, pos)) = tracker.get_next_available(tag, values) {
255 tracker.mark_consumed(tag, pos);
256 return Some((value.to_string(), Some(variant_str), pos));
257 }
258 }
259
260 None
261}
262
263pub struct SwiftParser {
289 config: ParserConfig,
290}
291
292impl Default for SwiftParser {
293 fn default() -> Self {
294 Self::new()
295 }
296}
297
298impl SwiftParser {
299 pub fn new() -> Self {
301 Self {
302 config: ParserConfig::default(),
303 }
304 }
305
306 pub fn with_config(config: ParserConfig) -> Self {
308 Self { config }
309 }
310
311 pub fn parse_with_errors<T: SwiftMessageBody>(
313 &self,
314 raw_message: &str,
315 ) -> Result<crate::errors::ParseResult<SwiftMessage<T>>> {
316 let block1 = Self::extract_block(raw_message, 1)?;
317 let block2 = Self::extract_block(raw_message, 2)?;
318 let block3 = Self::extract_block(raw_message, 3)?;
319 let block4 = Self::extract_block(raw_message, 4)?;
320 let block5 = Self::extract_block(raw_message, 5)?;
321
322 let basic_header = BasicHeader::parse(&block1.unwrap_or_default())?;
324 let application_header = ApplicationHeader::parse(&block2.unwrap_or_default())?;
325 let user_header = block3.map(|b| UserHeader::parse(&b)).transpose()?;
326 let trailer = block5.map(|b| Trailer::parse(&b)).transpose()?;
327
328 let message_type = application_header.message_type.clone();
330
331 if message_type != T::message_type() {
333 return Err(ParseError::SwiftValidation(Box::new(
334 SwiftValidationError::format_error(
335 t_series::T03,
336 "MESSAGE_TYPE",
337 &message_type,
338 T::message_type(),
339 &format!(
340 "Message type mismatch: expected {}, got {}",
341 T::message_type(),
342 message_type
343 ),
344 ),
345 )));
346 }
347
348 let field_map_with_positions = Self::parse_block4_fields(&block4.unwrap_or_default())?;
350
351 let parse_result = T::from_fields_with_config(field_map_with_positions, &self.config)?;
353
354 match parse_result {
355 crate::errors::ParseResult::Success(fields) => {
356 Ok(crate::errors::ParseResult::Success(SwiftMessage {
357 basic_header,
358 application_header,
359 user_header,
360 trailer,
361 message_type,
362 fields,
363 }))
364 }
365 crate::errors::ParseResult::PartialSuccess(fields, errors) => {
366 Ok(crate::errors::ParseResult::PartialSuccess(
367 SwiftMessage {
368 basic_header,
369 application_header,
370 user_header,
371 trailer,
372 message_type,
373 fields,
374 },
375 errors,
376 ))
377 }
378 crate::errors::ParseResult::Failure(errors) => {
379 Ok(crate::errors::ParseResult::Failure(errors))
380 }
381 }
382 }
383 pub fn parse<T: SwiftMessageBody>(raw_message: &str) -> Result<SwiftMessage<T>> {
385 Self::new().parse_message(raw_message)
386 }
387
388 pub fn parse_message<T: SwiftMessageBody>(&self, raw_message: &str) -> Result<SwiftMessage<T>> {
390 let block1 = Self::extract_block(raw_message, 1)?;
391 let block2 = Self::extract_block(raw_message, 2)?;
392 let block3 = Self::extract_block(raw_message, 3)?;
393 let block4 = Self::extract_block(raw_message, 4)?;
394 let block5 = Self::extract_block(raw_message, 5)?;
395
396 let basic_header = BasicHeader::parse(&block1.unwrap_or_default())?;
398 let application_header = ApplicationHeader::parse(&block2.unwrap_or_default())?;
399 let user_header = block3.map(|b| UserHeader::parse(&b)).transpose()?;
400 let trailer = block5.map(|b| Trailer::parse(&b)).transpose()?;
401
402 let message_type = application_header.message_type.clone();
404
405 if message_type != T::message_type() {
407 return Err(ParseError::SwiftValidation(Box::new(
408 SwiftValidationError::format_error(
409 t_series::T03,
410 "MESSAGE_TYPE",
411 &message_type,
412 T::message_type(),
413 &format!(
414 "Message type mismatch: expected {}, got {}",
415 T::message_type(),
416 message_type
417 ),
418 ),
419 )));
420 }
421
422 let field_map_with_positions = Self::parse_block4_fields(&block4.unwrap_or_default())?;
424
425 let parse_result = T::from_fields_with_config(field_map_with_positions, &self.config)?;
427
428 match parse_result {
429 crate::errors::ParseResult::Success(fields) => Ok(SwiftMessage {
430 basic_header,
431 application_header,
432 user_header,
433 trailer,
434 message_type,
435 fields,
436 }),
437 crate::errors::ParseResult::PartialSuccess(fields, errors) => {
438 eprintln!("Warning: Parsed with {} non-critical errors", errors.len());
441 for error in &errors {
442 eprintln!(" - {error}");
443 }
444 Ok(SwiftMessage {
445 basic_header,
446 application_header,
447 user_header,
448 trailer,
449 message_type,
450 fields,
451 })
452 }
453 crate::errors::ParseResult::Failure(errors) => {
454 Err(ParseError::MultipleErrors(errors))
456 }
457 }
458 }
459
460 pub fn parse_auto(raw_message: &str) -> Result<ParsedSwiftMessage> {
462 Self::new().parse_message_auto(raw_message)
463 }
464
465 pub fn parse_message_auto(&self, raw_message: &str) -> Result<ParsedSwiftMessage> {
467 let block2 = Self::extract_block(raw_message, 2)?;
469
470 let application_header = ApplicationHeader::parse(&block2.unwrap_or_default())?;
472 let message_type = &application_header.message_type;
473
474 match message_type.as_str() {
476 "101" => {
477 let parsed = self.parse_message::<MT101>(raw_message)?;
478 Ok(ParsedSwiftMessage::MT101(Box::new(parsed)))
479 }
480 "103" => {
481 let parsed = self.parse_message::<MT103>(raw_message)?;
482 Ok(ParsedSwiftMessage::MT103(Box::new(parsed)))
483 }
484 "104" => {
485 let parsed = self.parse_message::<MT104>(raw_message)?;
486 Ok(ParsedSwiftMessage::MT104(Box::new(parsed)))
487 }
488 "107" => {
489 let parsed = self.parse_message::<MT107>(raw_message)?;
490 Ok(ParsedSwiftMessage::MT107(Box::new(parsed)))
491 }
492 "110" => {
493 let parsed = self.parse_message::<MT110>(raw_message)?;
494 Ok(ParsedSwiftMessage::MT110(Box::new(parsed)))
495 }
496 "111" => {
497 let parsed = self.parse_message::<MT111>(raw_message)?;
498 Ok(ParsedSwiftMessage::MT111(Box::new(parsed)))
499 }
500 "112" => {
501 let parsed = self.parse_message::<MT112>(raw_message)?;
502 Ok(ParsedSwiftMessage::MT112(Box::new(parsed)))
503 }
504 "202" => {
505 let parsed = self.parse_message::<MT202>(raw_message)?;
506 Ok(ParsedSwiftMessage::MT202(Box::new(parsed)))
507 }
508 "205" => {
509 let parsed = self.parse_message::<MT205>(raw_message)?;
510 Ok(ParsedSwiftMessage::MT205(Box::new(parsed)))
511 }
512 "210" => {
513 let parsed = self.parse_message::<MT210>(raw_message)?;
514 Ok(ParsedSwiftMessage::MT210(Box::new(parsed)))
515 }
516 "900" => {
517 let parsed = self.parse_message::<MT900>(raw_message)?;
518 Ok(ParsedSwiftMessage::MT900(Box::new(parsed)))
519 }
520 "910" => {
521 let parsed = self.parse_message::<MT910>(raw_message)?;
522 Ok(ParsedSwiftMessage::MT910(Box::new(parsed)))
523 }
524 "920" => {
525 let parsed = self.parse_message::<MT920>(raw_message)?;
526 Ok(ParsedSwiftMessage::MT920(Box::new(parsed)))
527 }
528 "935" => {
529 let parsed = self.parse_message::<MT935>(raw_message)?;
530 Ok(ParsedSwiftMessage::MT935(Box::new(parsed)))
531 }
532 "940" => {
533 let parsed = self.parse_message::<MT940>(raw_message)?;
534 Ok(ParsedSwiftMessage::MT940(Box::new(parsed)))
535 }
536 "941" => {
537 let parsed = self.parse_message::<MT941>(raw_message)?;
538 Ok(ParsedSwiftMessage::MT941(Box::new(parsed)))
539 }
540 "942" => {
541 let parsed = self.parse_message::<MT942>(raw_message)?;
542 Ok(ParsedSwiftMessage::MT942(Box::new(parsed)))
543 }
544 "950" => {
545 let parsed = self.parse_message::<MT950>(raw_message)?;
546 Ok(ParsedSwiftMessage::MT950(Box::new(parsed)))
547 }
548 "192" => {
549 let parsed = self.parse_message::<MT192>(raw_message)?;
550 Ok(ParsedSwiftMessage::MT192(Box::new(parsed)))
551 }
552 "196" => {
553 let parsed = self.parse_message::<MT196>(raw_message)?;
554 Ok(ParsedSwiftMessage::MT196(Box::new(parsed)))
555 }
556 "292" => {
557 let parsed = self.parse_message::<MT292>(raw_message)?;
558 Ok(ParsedSwiftMessage::MT292(Box::new(parsed)))
559 }
560 "296" => {
561 let parsed = self.parse_message::<MT296>(raw_message)?;
562 Ok(ParsedSwiftMessage::MT296(Box::new(parsed)))
563 }
564 "199" => {
565 let parsed = self.parse_message::<MT199>(raw_message)?;
566 Ok(ParsedSwiftMessage::MT199(Box::new(parsed)))
567 }
568 "299" => {
569 let parsed = self.parse_message::<MT299>(raw_message)?;
570 Ok(ParsedSwiftMessage::MT299(Box::new(parsed)))
571 }
572 _ => Err(ParseError::UnsupportedMessageType {
573 message_type: message_type.clone(),
574 }),
575 }
576 }
577
578 pub fn extract_block(raw_message: &str, block_index: u8) -> Result<Option<String>> {
580 if !(1..=5).contains(&block_index) {
582 return Err(ParseError::SwiftValidation(Box::new(
583 crate::errors::SwiftValidationError::format_error(
584 crate::swift_error_codes::t_series::T01,
585 "BLOCK_INDEX",
586 &block_index.to_string(),
587 "1-5",
588 &format!("Invalid block index: {block_index}"),
589 ),
590 )));
591 }
592
593 let block_marker = format!("{{{block_index}:");
594
595 if let Some(start) = raw_message.find(&block_marker) {
596 let content_start = start + block_marker.len();
597
598 match block_index {
599 1 | 2 => {
600 if let Some(end) = raw_message[start..].find('}') {
602 let end = start + end;
603 Ok(Some(raw_message[content_start..end].to_string()))
604 } else {
605 Ok(None)
606 }
607 }
608 3 | 5 => {
609 if let Some(end) = Self::find_matching_brace(&raw_message[start..]) {
611 let end = start + end;
612 Ok(Some(raw_message[content_start..end].to_string()))
613 } else {
614 Ok(None)
615 }
616 }
617 4 => {
618 if let Some(end) = raw_message[start..].find("-}") {
620 let end = start + end;
621 Ok(Some(raw_message[content_start..end].to_string()))
622 } else {
623 Ok(None)
624 }
625 }
626 _ => Err(ParseError::SwiftValidation(Box::new(
627 crate::errors::SwiftValidationError::format_error(
628 crate::swift_error_codes::t_series::T02,
629 "BLOCK",
630 &block_index.to_string(),
631 "1-5",
632 &format!("Invalid block index: {block_index}"),
633 ),
634 ))),
635 }
636 } else {
637 Ok(None)
638 }
639 }
640
641 fn parse_block4_fields(block4: &str) -> FieldParseResult {
643 crate::parser::parse_block4_fields(block4)
645 }
646
647 fn find_matching_brace(text: &str) -> Option<usize> {
650 let mut chars = text.char_indices();
651
652 let mut brace_count = if let Some((_, '{')) = chars.next() {
654 1
655 } else {
656 return None;
657 };
658
659 for (i, ch) in chars {
660 match ch {
661 '{' => brace_count += 1,
662 '}' => {
663 brace_count -= 1;
664 if brace_count == 0 {
665 return Some(i);
666 }
667 }
668 _ => {}
669 }
670 }
671
672 None
673 }
674}
675
676pub fn parse_swift_message_from_string(value: &str) -> Result<HashMap<String, Vec<String>>> {
679 let mut field_map = HashMap::new();
686
687 for line in value.lines() {
689 if line.trim().is_empty() {
690 continue;
691 }
692
693 if let Some(colon_pos) = line.find(':') {
695 if let Some(second_colon) = line[colon_pos + 1..].find(':') {
696 let second_colon_pos = colon_pos + 1 + second_colon;
697 let field_tag = line[colon_pos + 1..second_colon_pos].to_string();
698 let _field_value = line[second_colon_pos + 1..].to_string();
699
700 field_map
701 .entry(field_tag)
702 .or_insert_with(Vec::new)
703 .push(format!(":{}", &line[colon_pos + 1..]));
704 }
705 }
706 }
707
708 Ok(field_map)
709}
710
711pub fn parse_sequences<T>(
717 fields: &HashMap<String, Vec<(String, usize)>>,
718 tracker: &mut FieldConsumptionTracker,
719) -> Result<Vec<T>>
720where
721 T: crate::SwiftMessageBody,
722{
723 let message_type = std::any::type_name::<T>();
725
726 if message_type.contains("MT104Transaction") {
727 use crate::parser::sequence_parser::{get_sequence_config, split_into_sequences};
729
730 let config = get_sequence_config("MT104");
731 let parsed_sequences = split_into_sequences(fields, &config)?;
732
733 return parse_sequence_b_items::<T>(&parsed_sequences.sequence_b, tracker);
735 }
736
737 let mut all_fields: Vec<(String, String, usize)> = Vec::new();
739 for (tag, values) in fields {
740 for (value, pos) in values {
741 if tracker
743 .consumed_indices
744 .get(tag)
745 .is_none_or(|set| !set.contains(pos))
746 {
747 all_fields.push((tag.clone(), value.clone(), *pos));
748 }
749 }
750 }
751 all_fields.sort_by_key(|(_, _, pos)| *pos);
752
753 let (primary_marker, secondary_marker) = if message_type.contains("MT920Sequence") {
755 ("12", None)
756 } else if message_type.contains("MT935RateChange") {
757 ("23", Some("25"))
758 } else if message_type.contains("MT940StatementLine")
759 || message_type.contains("MT942StatementLine")
760 {
761 ("61", None)
762 } else {
763 ("21", None)
764 };
765
766 let mut sequences = Vec::new();
767 let mut current_sequence_fields: HashMap<String, Vec<(String, usize)>> = HashMap::new();
768 let mut in_sequence = false;
769
770 for (tag, value, pos) in all_fields {
771 let is_sequence_start = (tag == primary_marker
773 || secondary_marker.is_some_and(|m| tag == m))
774 && !tag.ends_with("R")
775 && !tag.ends_with("F")
776 && !tag.ends_with("C")
777 && !tag.ends_with("D");
778
779 if is_sequence_start {
780 if in_sequence && !current_sequence_fields.is_empty() {
782 if let Ok(sequence_item) = T::from_fields(current_sequence_fields.clone()) {
783 sequences.push(sequence_item);
784 }
785 current_sequence_fields.clear();
786 }
787 in_sequence = true;
788 }
789
790 if in_sequence {
792 current_sequence_fields
793 .entry(tag.clone())
794 .or_default()
795 .push((value, pos));
796
797 tracker.mark_consumed(&tag, pos);
799 }
800 }
801
802 if in_sequence && !current_sequence_fields.is_empty() {
804 match T::from_fields(current_sequence_fields) {
805 Ok(sequence_item) => {
806 sequences.push(sequence_item);
807 }
808 Err(_e) => {
809 #[cfg(debug_assertions)]
810 eprintln!("DEBUG: Failed to parse final sequence item: {_e:?}");
811 }
812 }
813 }
814
815 Ok(sequences)
816}
817
818fn parse_sequence_b_items<T>(
820 fields: &HashMap<String, Vec<(String, usize)>>,
821 tracker: &mut FieldConsumptionTracker,
822) -> Result<Vec<T>>
823where
824 T: crate::SwiftMessageBody,
825{
826 let mut sequences = Vec::new();
827
828 let mut all_fields: Vec<(String, String, usize)> = Vec::new();
830 for (tag, values) in fields {
831 for (value, pos) in values {
832 all_fields.push((tag.clone(), value.clone(), *pos));
833 }
834 }
835 all_fields.sort_by_key(|(_, _, pos)| *pos);
836
837 let sequence_start_tag = "21";
838 let mut current_sequence_fields: HashMap<String, Vec<(String, usize)>> = HashMap::new();
839 let mut in_sequence = false;
840
841 for (tag, value, pos) in all_fields {
842 if tag == sequence_start_tag
844 && !tag.ends_with("R")
845 && !tag.ends_with("F")
846 && !tag.ends_with("C")
847 && !tag.ends_with("D")
848 {
849 if in_sequence && !current_sequence_fields.is_empty() {
851 if let Ok(sequence_item) = T::from_fields(current_sequence_fields.clone()) {
852 sequences.push(sequence_item);
853 }
854 current_sequence_fields.clear();
855 }
856 in_sequence = true;
857 }
858
859 if in_sequence {
861 current_sequence_fields
862 .entry(tag.clone())
863 .or_default()
864 .push((value, pos));
865
866 tracker.mark_consumed(&tag, pos);
868 }
869 }
870
871 if in_sequence && !current_sequence_fields.is_empty() {
873 match T::from_fields(current_sequence_fields) {
874 Ok(sequence_item) => {
875 sequences.push(sequence_item);
876 }
877 Err(_e) => {
878 #[cfg(debug_assertions)]
879 eprintln!("DEBUG: Failed to parse final sequence item: {_e:?}");
880 }
881 }
882 }
883
884 Ok(sequences)
885}
886
887pub fn serialize_swift_message_to_string(fields: &HashMap<String, Vec<String>>) -> String {
890 let mut result = String::new();
895
896 for field_values in fields.values() {
898 for field_value in field_values {
899 result.push_str(field_value);
901 result.push('\n');
902 }
903 }
904
905 if result.ends_with('\n') {
907 result.pop();
908 }
909
910 result
911}