1use std::collections::{HashMap, HashSet};
46
47use crate::errors::{ParseError, Result, SwiftValidationError};
48use crate::headers::{ApplicationHeader, BasicHeader, Trailer, UserHeader};
49use crate::messages::{
50 MT101, MT103, MT104, MT107, MT110, MT111, MT112, MT190, MT191, MT192, MT196, MT199, MT200,
51 MT202, MT204, MT205, MT210, MT290, MT291, MT292, MT296, MT299, MT900, MT910, MT920, MT935,
52 MT940, MT941, MT942, MT950,
53};
54use crate::swift_error_codes::t_series;
55use crate::{ParsedSwiftMessage, SwiftMessage, SwiftMessageBody};
56
57#[derive(Debug, Clone)]
59pub struct ParsingContext {
60 pub current_field: Option<String>,
62 pub current_component: Option<String>,
64 pub message_type: String,
66 pub original_message: String,
68}
69
70impl ParsingContext {
71 pub fn new(message_type: String, original_message: String) -> Self {
73 Self {
74 current_field: None,
75 current_component: None,
76 message_type,
77 original_message,
78 }
79 }
80
81 pub fn with_field(&self, field: String) -> Self {
83 let mut ctx = self.clone();
84 ctx.current_field = Some(field);
85 ctx.current_component = None;
86 ctx
87 }
88
89 pub fn with_component(&self, component: String) -> Self {
91 let mut ctx = self.clone();
92 ctx.current_component = Some(component);
93 ctx
94 }
95}
96
97#[derive(Debug, Clone)]
126pub struct FieldConsumptionTracker {
127 consumed_indices: HashMap<String, HashSet<usize>>,
129}
130
131impl Default for FieldConsumptionTracker {
132 fn default() -> Self {
133 Self::new()
134 }
135}
136
137impl FieldConsumptionTracker {
138 pub fn new() -> Self {
140 Self {
141 consumed_indices: HashMap::new(),
142 }
143 }
144
145 pub fn mark_consumed(&mut self, tag: &str, index: usize) {
147 use std::collections::hash_map::Entry;
149 match self.consumed_indices.entry(tag.to_string()) {
150 Entry::Occupied(mut e) => {
151 e.get_mut().insert(index);
152 }
153 Entry::Vacant(e) => {
154 let mut set = HashSet::new();
155 set.insert(index);
156 e.insert(set);
157 }
158 }
159 }
160
161 pub fn get_next_available<'a>(
163 &self,
164 tag: &str,
165 values: &'a [(String, usize)],
166 ) -> Option<(&'a str, usize)> {
167 let consumed_set = self.consumed_indices.get(tag);
168
169 values
171 .iter()
172 .find(|(_, pos)| consumed_set.is_none_or(|set| !set.contains(pos)))
173 .map(|(value, pos)| (value.as_str(), *pos))
174 }
175}
176
177#[derive(Debug, Clone)]
179enum FieldRoutingStrategy {
180 InstructingParty,
182 CreditorParty,
184}
185
186impl FieldRoutingStrategy {
187 fn get_preferred_variants(&self, base_tag: &str) -> Option<Vec<&'static str>> {
189 match (self, base_tag) {
190 (FieldRoutingStrategy::InstructingParty, "50") => Some(vec!["C", "L"]),
191 (FieldRoutingStrategy::CreditorParty, "50") => Some(vec!["A", "F", "K"]),
192 _ => None,
193 }
194 }
195}
196
197fn apply_field50_routing_strategy<'a>(
209 mut candidates: Vec<(&'a String, &'a Vec<(String, usize)>)>,
210 tracker: &FieldConsumptionTracker,
211 base_tag: &str,
212) -> Vec<(&'a String, &'a Vec<(String, usize)>)> {
213 let consumed_field50_count = candidates
215 .iter()
216 .filter(|(tag, _)| {
217 tracker
218 .consumed_indices
219 .get(*tag)
220 .is_some_and(|set| !set.is_empty())
221 })
222 .count();
223
224 #[cfg(debug_assertions)]
225 eprintln!(
226 "DEBUG: Field50 routing strategy - consumed_count={}, candidates={:?}",
227 consumed_field50_count,
228 candidates
229 .iter()
230 .map(|(tag, _)| tag.as_str())
231 .collect::<Vec<_>>()
232 );
233
234 let strategy = if consumed_field50_count == 0 {
236 FieldRoutingStrategy::InstructingParty
238 } else {
239 FieldRoutingStrategy::CreditorParty
241 };
242
243 if let Some(preferred_variants) = strategy.get_preferred_variants(base_tag) {
244 candidates.sort_by_key(|(tag, _)| {
246 let variant_char = tag.chars().last().unwrap_or(' ');
247 let variant_str = variant_char.to_string();
248
249 let is_preferred = preferred_variants.contains(&variant_str.as_str());
251
252 if is_preferred {
253 0 } else {
255 1 }
257 });
258
259 #[cfg(debug_assertions)]
260 eprintln!(
261 "DEBUG: Field50 routing - strategy={:?}, preferred_variants={:?}, reordered_candidates={:?}",
262 strategy,
263 preferred_variants,
264 candidates
265 .iter()
266 .map(|(tag, _)| tag.as_str())
267 .collect::<Vec<_>>()
268 );
269 }
270
271 candidates
272}
273
274pub fn find_field_with_variant_sequential_numbered(
290 fields: &HashMap<String, Vec<(String, usize)>>,
291 base_tag: &str,
292 tracker: &mut FieldConsumptionTracker,
293 valid_variants: Option<Vec<&str>>,
294 _numbered_tag: &str,
295) -> Option<(String, Option<String>, usize)> {
296 #[cfg(debug_assertions)]
297 eprintln!(
298 "DEBUG: find_field_with_variant_sequential_numbered for tag={}, base={}, variants={:?}",
299 _numbered_tag, base_tag, valid_variants
300 );
301
302 find_field_with_variant_sequential_constrained(
306 fields,
307 base_tag,
308 tracker,
309 valid_variants.as_deref(),
310 )
311}
312
313pub fn find_field_with_variant_sequential_constrained(
332 fields: &HashMap<String, Vec<(String, usize)>>,
333 base_tag: &str,
334 tracker: &mut FieldConsumptionTracker,
335 valid_variants: Option<&[&str]>,
336) -> Option<(String, Option<String>, usize)> {
337 #[cfg(debug_assertions)]
338 {
339 eprintln!(
340 "DEBUG: find_field_with_variant called for base_tag={}, valid_variants={:?}",
341 base_tag, valid_variants
342 );
343 eprintln!(
344 " Available fields: {:?}",
345 fields.keys().collect::<Vec<_>>()
346 );
347
348 if base_tag == "50" {
350 eprintln!(
351 "DEBUG: Field50 routing - base_tag={}, constraints={:?}",
352 base_tag, valid_variants
353 );
354 eprintln!(
355 "DEBUG: Available Field50 variants: {:?}",
356 fields
357 .keys()
358 .filter(|k| k.starts_with("50") && k.len() == 3)
359 .collect::<Vec<_>>()
360 );
361 }
362 }
363
364 if let Some(values) = fields.get(base_tag)
366 && let Some((value, pos)) = tracker.get_next_available(base_tag, values)
367 {
368 #[cfg(debug_assertions)]
369 {
370 eprintln!(
371 "DEBUG: Found exact match for base_tag={}, marking as consumed at pos={}",
372 base_tag, pos
373 );
374 if base_tag == "90D" || base_tag == "86" {
375 eprintln!(
376 "DEBUG: Returning value for {}: '{}'",
377 base_tag,
378 if value.len() > 50 {
379 &value[..50]
380 } else {
381 value
382 }
383 );
384 }
385 }
386 tracker.mark_consumed(base_tag, pos);
387 return Some((value.to_string(), None, pos));
388 }
389
390 let mut variant_candidates: Vec<(&String, &Vec<(String, usize)>)> = fields
393 .iter()
394 .filter(|(tag, _)| {
395 tag.starts_with(base_tag)
396 && tag.len() == base_tag.len() + 1
397 && tag
398 .chars()
399 .last()
400 .is_some_and(|c| c.is_ascii_alphabetic() && c.is_ascii_uppercase())
401 })
402 .collect();
403
404 if base_tag == "50" && valid_variants.is_some() {
406 variant_candidates = apply_field50_routing_strategy(variant_candidates, tracker, base_tag);
408 }
409
410 variant_candidates.sort_by_key(|(tag, values)| {
412 values
413 .iter()
414 .filter(|(_, pos)| {
415 tracker
416 .consumed_indices
417 .get(*tag)
418 .is_none_or(|set| !set.contains(pos))
419 })
420 .map(|(_, pos)| *pos)
421 .min()
422 .unwrap_or(usize::MAX)
423 });
424
425 for (tag, values) in variant_candidates {
426 let variant_char = tag.chars().last().unwrap();
427 let variant_str = variant_char.to_string();
428
429 if let Some(valid) = valid_variants
431 && !valid.contains(&variant_str.as_str())
432 {
433 #[cfg(debug_assertions)]
434 eprintln!(
435 "DEBUG: Skipping field {} with variant {} - not in valid variants {:?}",
436 tag, variant_str, valid
437 );
438 continue; }
440
441 if let Some((value, pos)) = tracker.get_next_available(tag, values) {
442 #[cfg(debug_assertions)]
443 eprintln!(
444 "DEBUG: Found field {} with variant {} for base tag {}",
445 tag, variant_str, base_tag
446 );
447 tracker.mark_consumed(tag, pos);
448 return Some((value.to_string(), Some(variant_str), pos));
449 }
450 }
451
452 None
453}
454
455pub struct SwiftParser {}
481
482impl Default for SwiftParser {
483 fn default() -> Self {
484 Self::new()
485 }
486}
487
488impl SwiftParser {
489 pub fn new() -> Self {
491 Self {}
492 }
493
494 pub fn parse_with_errors<T: SwiftMessageBody>(
496 &self,
497 raw_message: &str,
498 ) -> Result<crate::errors::ParseResult<SwiftMessage<T>>> {
499 let block1 = Self::extract_block(raw_message, 1)?;
500 let block2 = Self::extract_block(raw_message, 2)?;
501 let block3 = Self::extract_block(raw_message, 3)?;
502 let block4 = Self::extract_block(raw_message, 4)?;
503 let block5 = Self::extract_block(raw_message, 5)?;
504
505 let basic_header = BasicHeader::parse(&block1.unwrap_or_default())?;
507 let application_header = ApplicationHeader::parse(&block2.unwrap_or_default())?;
508 let user_header = block3.map(|b| UserHeader::parse(&b)).transpose()?;
509 let trailer = block5.map(|b| Trailer::parse(&b)).transpose()?;
510
511 let message_type = application_header.message_type().to_string();
513
514 if message_type != T::message_type() {
516 return Err(ParseError::SwiftValidation(Box::new(
517 SwiftValidationError::format_error(
518 t_series::T03,
519 "MESSAGE_TYPE",
520 &message_type,
521 T::message_type(),
522 &format!(
523 "Message type mismatch: expected {}, got {}",
524 T::message_type(),
525 message_type
526 ),
527 ),
528 )));
529 }
530
531 let fields = T::parse_from_block4(&block4.unwrap_or_default())?;
533
534 Ok(crate::errors::ParseResult::Success(SwiftMessage {
535 basic_header,
536 application_header,
537 user_header,
538 trailer,
539 message_type,
540 fields,
541 }))
542 }
543 pub fn parse<T: SwiftMessageBody>(raw_message: &str) -> Result<SwiftMessage<T>> {
545 Self::new().parse_message(raw_message)
546 }
547
548 pub fn parse_message<T: SwiftMessageBody>(&self, raw_message: &str) -> Result<SwiftMessage<T>> {
550 let block1 = Self::extract_block(raw_message, 1)?;
551 let block2 = Self::extract_block(raw_message, 2)?;
552 let block3 = Self::extract_block(raw_message, 3)?;
553 let block4 = Self::extract_block(raw_message, 4)?;
554 let block5 = Self::extract_block(raw_message, 5)?;
555
556 let basic_header = BasicHeader::parse(&block1.unwrap_or_default())?;
558 let application_header = ApplicationHeader::parse(&block2.unwrap_or_default())?;
559 let user_header = block3.map(|b| UserHeader::parse(&b)).transpose()?;
560 let trailer = block5.map(|b| Trailer::parse(&b)).transpose()?;
561
562 let message_type = application_header.message_type().to_string();
564
565 if message_type != T::message_type() {
567 return Err(ParseError::SwiftValidation(Box::new(
568 SwiftValidationError::format_error(
569 t_series::T03,
570 "MESSAGE_TYPE",
571 &message_type,
572 T::message_type(),
573 &format!(
574 "Message type mismatch: expected {}, got {}",
575 T::message_type(),
576 message_type
577 ),
578 ),
579 )));
580 }
581
582 let fields = T::parse_from_block4(&block4.unwrap_or_default())?;
584
585 Ok(SwiftMessage {
586 basic_header,
587 application_header,
588 user_header,
589 trailer,
590 message_type,
591 fields,
592 })
593 }
594
595 pub fn parse_auto(raw_message: &str) -> Result<ParsedSwiftMessage> {
597 Self::new().parse_message_auto(raw_message)
598 }
599
600 pub fn parse_message_auto(&self, raw_message: &str) -> Result<ParsedSwiftMessage> {
602 let block2 = Self::extract_block(raw_message, 2)?;
604
605 let application_header = ApplicationHeader::parse(&block2.unwrap_or_default())?;
607 let message_type = application_header.message_type();
608
609 match message_type {
611 "101" => {
612 let parsed = self.parse_message::<MT101>(raw_message)?;
613 Ok(ParsedSwiftMessage::MT101(Box::new(parsed)))
614 }
615 "103" => {
616 let parsed = self.parse_message::<MT103>(raw_message)?;
617 Ok(ParsedSwiftMessage::MT103(Box::new(parsed)))
618 }
619 "104" => {
620 let parsed = self.parse_message::<MT104>(raw_message)?;
621 Ok(ParsedSwiftMessage::MT104(Box::new(parsed)))
622 }
623 "107" => {
624 let parsed = self.parse_message::<MT107>(raw_message)?;
625 Ok(ParsedSwiftMessage::MT107(Box::new(parsed)))
626 }
627 "110" => {
628 let parsed = self.parse_message::<MT110>(raw_message)?;
629 Ok(ParsedSwiftMessage::MT110(Box::new(parsed)))
630 }
631 "111" => {
632 let parsed = self.parse_message::<MT111>(raw_message)?;
633 Ok(ParsedSwiftMessage::MT111(Box::new(parsed)))
634 }
635 "112" => {
636 let parsed = self.parse_message::<MT112>(raw_message)?;
637 Ok(ParsedSwiftMessage::MT112(Box::new(parsed)))
638 }
639 "190" => {
640 let parsed = self.parse_message::<MT190>(raw_message)?;
641 Ok(ParsedSwiftMessage::MT190(Box::new(parsed)))
642 }
643 "191" => {
644 let parsed = self.parse_message::<MT191>(raw_message)?;
645 Ok(ParsedSwiftMessage::MT191(Box::new(parsed)))
646 }
647 "200" => {
648 let parsed = self.parse_message::<MT200>(raw_message)?;
649 Ok(ParsedSwiftMessage::MT200(Box::new(parsed)))
650 }
651 "202" => {
652 let parsed = self.parse_message::<MT202>(raw_message)?;
653 Ok(ParsedSwiftMessage::MT202(Box::new(parsed)))
654 }
655 "204" => {
656 let parsed = self.parse_message::<MT204>(raw_message)?;
657 Ok(ParsedSwiftMessage::MT204(Box::new(parsed)))
658 }
659 "205" => {
660 let parsed = self.parse_message::<MT205>(raw_message)?;
661 Ok(ParsedSwiftMessage::MT205(Box::new(parsed)))
662 }
663 "210" => {
664 let parsed = self.parse_message::<MT210>(raw_message)?;
665 Ok(ParsedSwiftMessage::MT210(Box::new(parsed)))
666 }
667 "290" => {
668 let parsed = self.parse_message::<MT290>(raw_message)?;
669 Ok(ParsedSwiftMessage::MT290(Box::new(parsed)))
670 }
671 "291" => {
672 let parsed = self.parse_message::<MT291>(raw_message)?;
673 Ok(ParsedSwiftMessage::MT291(Box::new(parsed)))
674 }
675 "900" => {
676 let parsed = self.parse_message::<MT900>(raw_message)?;
677 Ok(ParsedSwiftMessage::MT900(Box::new(parsed)))
678 }
679 "910" => {
680 let parsed = self.parse_message::<MT910>(raw_message)?;
681 Ok(ParsedSwiftMessage::MT910(Box::new(parsed)))
682 }
683 "920" => {
684 let parsed = self.parse_message::<MT920>(raw_message)?;
685 Ok(ParsedSwiftMessage::MT920(Box::new(parsed)))
686 }
687 "935" => {
688 let parsed = self.parse_message::<MT935>(raw_message)?;
689 Ok(ParsedSwiftMessage::MT935(Box::new(parsed)))
690 }
691 "940" => {
692 let parsed = self.parse_message::<MT940>(raw_message)?;
693 Ok(ParsedSwiftMessage::MT940(Box::new(parsed)))
694 }
695 "941" => {
696 let parsed = self.parse_message::<MT941>(raw_message)?;
697 Ok(ParsedSwiftMessage::MT941(Box::new(parsed)))
698 }
699 "942" => {
700 let parsed = self.parse_message::<MT942>(raw_message)?;
701 Ok(ParsedSwiftMessage::MT942(Box::new(parsed)))
702 }
703 "950" => {
704 let parsed = self.parse_message::<MT950>(raw_message)?;
705 Ok(ParsedSwiftMessage::MT950(Box::new(parsed)))
706 }
707 "192" => {
708 let parsed = self.parse_message::<MT192>(raw_message)?;
709 Ok(ParsedSwiftMessage::MT192(Box::new(parsed)))
710 }
711 "196" => {
712 let parsed = self.parse_message::<MT196>(raw_message)?;
713 Ok(ParsedSwiftMessage::MT196(Box::new(parsed)))
714 }
715 "292" => {
716 let parsed = self.parse_message::<MT292>(raw_message)?;
717 Ok(ParsedSwiftMessage::MT292(Box::new(parsed)))
718 }
719 "296" => {
720 let parsed = self.parse_message::<MT296>(raw_message)?;
721 Ok(ParsedSwiftMessage::MT296(Box::new(parsed)))
722 }
723 "199" => {
724 let parsed = self.parse_message::<MT199>(raw_message)?;
725 Ok(ParsedSwiftMessage::MT199(Box::new(parsed)))
726 }
727 "299" => {
728 let parsed = self.parse_message::<MT299>(raw_message)?;
729 Ok(ParsedSwiftMessage::MT299(Box::new(parsed)))
730 }
731 _ => Err(ParseError::UnsupportedMessageType {
732 message_type: message_type.to_string(),
733 }),
734 }
735 }
736
737 pub fn extract_block(raw_message: &str, block_index: u8) -> Result<Option<String>> {
739 if !(1..=5).contains(&block_index) {
741 return Err(ParseError::SwiftValidation(Box::new(
742 crate::errors::SwiftValidationError::format_error(
743 crate::swift_error_codes::t_series::T01,
744 "BLOCK_INDEX",
745 &block_index.to_string(),
746 "1-5",
747 &format!("Invalid block index: {block_index}"),
748 ),
749 )));
750 }
751
752 let block_marker = format!("{{{block_index}:");
753
754 if let Some(start) = raw_message.find(&block_marker) {
755 let content_start = start + block_marker.len();
756
757 match block_index {
758 1 | 2 => {
759 if let Some(end) = raw_message[start..].find('}') {
761 let end = start + end;
762 Ok(Some(raw_message[content_start..end].to_string()))
763 } else {
764 Ok(None)
765 }
766 }
767 3 | 5 => {
768 if let Some(end) = Self::find_matching_brace(&raw_message[start..]) {
770 let end = start + end;
771 Ok(Some(raw_message[content_start..end].to_string()))
772 } else {
773 Ok(None)
774 }
775 }
776 4 => {
777 if let Some(end) = raw_message[start..].find("-}") {
779 let end = start + end;
780 Ok(Some(raw_message[content_start..end].to_string()))
781 } else {
782 Ok(None)
783 }
784 }
785 _ => Err(ParseError::SwiftValidation(Box::new(
786 crate::errors::SwiftValidationError::format_error(
787 crate::swift_error_codes::t_series::T02,
788 "BLOCK",
789 &block_index.to_string(),
790 "1-5",
791 &format!("Invalid block index: {block_index}"),
792 ),
793 ))),
794 }
795 } else {
796 Ok(None)
797 }
798 }
799
800 fn find_matching_brace(text: &str) -> Option<usize> {
803 let mut chars = text.char_indices();
804
805 let mut brace_count = if let Some((_, '{')) = chars.next() {
807 1
808 } else {
809 return None;
810 };
811
812 for (i, ch) in chars {
813 match ch {
814 '{' => brace_count += 1,
815 '}' => {
816 brace_count -= 1;
817 if brace_count == 0 {
818 return Some(i);
819 }
820 }
821 _ => {}
822 }
823 }
824
825 None
826 }
827}
828
829fn reconstruct_block4_from_fields(fields: &HashMap<String, Vec<(String, usize)>>) -> String {
834 let mut all_fields: Vec<(&str, &str, usize)> = Vec::new();
836 for (tag, values) in fields {
837 for (value, pos) in values {
838 all_fields.push((tag, value, *pos));
839 }
840 }
841 all_fields.sort_by_key(|(_, _, pos)| *pos);
842
843 let mut result = String::new();
845 for (tag, value, _) in all_fields {
846 let clean_value = if value.starts_with(&format!(":{tag}:")) {
848 &value[tag.len() + 2..]
849 } else if let Some(stripped) = value.strip_prefix(':') {
850 if let Some(second_colon) = stripped.find(':') {
852 &value[second_colon + 2..]
853 } else {
854 value
855 }
856 } else {
857 value
858 };
859
860 result.push_str(&format!(":{tag}:{clean_value}\n"));
861 }
862
863 result
864}
865
866pub fn parse_sequences<T>(
872 fields: &HashMap<String, Vec<(String, usize)>>,
873 tracker: &mut FieldConsumptionTracker,
874) -> Result<Vec<T>>
875where
876 T: crate::SwiftMessageBody,
877{
878 let message_type = std::any::type_name::<T>();
880
881 if message_type.contains("MT104Transaction") {
882 use crate::parser::sequence_parser::{get_sequence_config, split_into_sequences};
884
885 let config = get_sequence_config("MT104");
886 let parsed_sequences = split_into_sequences(fields, &config)?;
887
888 return parse_sequence_b_items::<T>(&parsed_sequences.sequence_b, tracker);
890 }
891
892 if message_type.contains("MT110Cheque") {
893 use crate::parser::sequence_parser::{get_sequence_config, split_into_sequences};
895
896 let config = get_sequence_config("MT110");
897 let parsed_sequences = split_into_sequences(fields, &config)?;
898
899 return parse_sequence_b_items::<T>(&parsed_sequences.sequence_b, tracker);
901 }
902
903 if message_type.contains("MT204Transaction") {
904 let field_20_count = fields.get("20").map(|v| v.len()).unwrap_or(0);
909 if field_20_count <= 1 {
910 return Ok(Vec::new()); }
912
913 let num_transactions = field_20_count - 1; let mut transactions = Vec::new();
917
918 for i in 0..num_transactions {
919 let mut tx_fields = HashMap::new();
920
921 if let Some(field_20_values) = fields.get("20")
923 && i + 1 < field_20_values.len()
924 {
925 tx_fields.insert("20".to_string(), vec![field_20_values[i + 1].clone()]);
926 }
927
928 if let Some(field_21_values) = fields.get("21")
930 && i < field_21_values.len()
931 {
932 tx_fields.insert("21".to_string(), vec![field_21_values[i].clone()]);
933 }
934
935 if let Some(field_32b_values) = fields.get("32B")
937 && i < field_32b_values.len()
938 {
939 tx_fields.insert("32B".to_string(), vec![field_32b_values[i].clone()]);
940 }
941
942 for variant in ["53", "53A", "53B", "53D"] {
944 if let Some(field_53_values) = fields.get(variant)
945 && i < field_53_values.len()
946 {
947 tx_fields.insert(variant.to_string(), vec![field_53_values[i].clone()]);
948 break; }
950 }
951
952 if let Some(field_72_values) = fields.get("72") {
955 if i + 1 < field_72_values.len() {
957 tx_fields.insert("72".to_string(), vec![field_72_values[i + 1].clone()]);
958 }
959 }
960
961 let block4_str = reconstruct_block4_from_fields(&tx_fields);
963 if let Ok(transaction) = T::parse_from_block4(&block4_str) {
964 transactions.push(transaction);
965 }
966 }
967
968 return Ok(transactions);
969 }
970
971 let mut all_fields: Vec<(String, String, usize)> = Vec::new();
973 for (tag, values) in fields {
974 for (value, pos) in values {
975 if tracker
977 .consumed_indices
978 .get(tag)
979 .is_none_or(|set| !set.contains(pos))
980 {
981 #[cfg(debug_assertions)]
982 if tag.starts_with("50") {
983 eprintln!(
984 "DEBUG: Collecting field for sequence: tag='{}' from fields HashMap",
985 tag
986 );
987 }
988 all_fields.push((tag.clone(), value.clone(), *pos));
989 }
990 }
991 }
992 all_fields.sort_by_key(|(_, _, pos)| *pos);
993
994 let (primary_marker, secondary_marker) = if message_type.contains("MT920Sequence") {
996 ("12", None)
997 } else if message_type.contains("MT935RateChange") {
998 ("23", Some("25"))
999 } else if message_type.contains("MT940StatementLine")
1000 || message_type.contains("MT942StatementLine")
1001 {
1002 ("61", None)
1003 } else {
1004 ("21", None)
1005 };
1006
1007 let mut sequences = Vec::new();
1008 let mut current_sequence_fields: HashMap<String, Vec<(String, usize)>> = HashMap::new();
1009 let mut in_sequence = false;
1010
1011 let is_mt942_statement = message_type.contains("MT942StatementLine");
1014
1015 let mut has_field_61_in_sequence = false;
1017 let mut has_field_86_in_sequence = false;
1018
1019 for (tag, value, pos) in all_fields {
1020 let is_sequence_start = (tag == primary_marker
1022 || secondary_marker.is_some_and(|m| tag == m))
1023 && !tag.ends_with("R")
1024 && !tag.ends_with("F")
1025 && !tag.ends_with("C")
1026 && !tag.ends_with("D");
1027
1028 if is_sequence_start {
1029 if in_sequence && !current_sequence_fields.is_empty() {
1031 let block4_str = reconstruct_block4_from_fields(¤t_sequence_fields);
1032 if let Ok(sequence_item) = T::parse_from_block4(&block4_str) {
1033 sequences.push(sequence_item);
1034 }
1035 current_sequence_fields.clear();
1036 }
1037 in_sequence = true;
1038 has_field_61_in_sequence = true; has_field_86_in_sequence = false;
1040 }
1041
1042 let should_include_in_sequence = if is_mt942_statement && in_sequence {
1044 if tag == "61" {
1045 if has_field_61_in_sequence && !current_sequence_fields.is_empty() {
1047 let block4_str = reconstruct_block4_from_fields(¤t_sequence_fields);
1049 if let Ok(sequence_item) = T::parse_from_block4(&block4_str) {
1050 sequences.push(sequence_item);
1051 }
1052 current_sequence_fields.clear();
1053 has_field_86_in_sequence = false;
1054 }
1055 has_field_61_in_sequence = true;
1056 true
1057 } else if tag == "86" && has_field_61_in_sequence && !has_field_86_in_sequence {
1058 has_field_86_in_sequence = true;
1060 true
1061 } else {
1062 false
1064 }
1065 } else if !is_mt942_statement && in_sequence {
1066 true
1068 } else {
1069 false
1070 };
1071
1072 if should_include_in_sequence {
1074 #[cfg(debug_assertions)]
1075 if tag.starts_with("50") || tag.starts_with("90") || tag.starts_with("86") {
1076 eprintln!(
1077 "DEBUG: Adding field to sequence: tag='{}', value_start='{}'",
1078 tag,
1079 value.lines().next().unwrap_or("")
1080 );
1081 }
1082 current_sequence_fields
1083 .entry(tag.clone())
1084 .or_default()
1085 .push((value, pos));
1086
1087 tracker.mark_consumed(&tag, pos);
1089 } else if is_mt942_statement && in_sequence && !should_include_in_sequence {
1090 if !current_sequence_fields.is_empty() {
1093 let block4_str = reconstruct_block4_from_fields(¤t_sequence_fields);
1094 if let Ok(sequence_item) = T::parse_from_block4(&block4_str) {
1095 sequences.push(sequence_item);
1096 }
1097 current_sequence_fields.clear();
1098 }
1099 in_sequence = false;
1100 has_field_61_in_sequence = false;
1101 has_field_86_in_sequence = false;
1102
1103 #[cfg(debug_assertions)]
1104 eprintln!("DEBUG: Ending MT942 sequence at field: tag='{}'", tag);
1105 }
1106 }
1107
1108 if in_sequence && !current_sequence_fields.is_empty() {
1110 let block4_str = reconstruct_block4_from_fields(¤t_sequence_fields);
1111 match T::parse_from_block4(&block4_str) {
1112 Ok(sequence_item) => {
1113 sequences.push(sequence_item);
1114 }
1115 Err(_e) => {
1116 #[cfg(debug_assertions)]
1117 eprintln!("DEBUG: Failed to parse final sequence item: {_e:?}");
1118 }
1119 }
1120 }
1121
1122 Ok(sequences)
1123}
1124
1125fn parse_sequence_b_items<T>(
1127 fields: &HashMap<String, Vec<(String, usize)>>,
1128 tracker: &mut FieldConsumptionTracker,
1129) -> Result<Vec<T>>
1130where
1131 T: crate::SwiftMessageBody,
1132{
1133 let mut sequences = Vec::new();
1134
1135 let mut all_fields: Vec<(String, String, usize)> = Vec::new();
1137 for (tag, values) in fields {
1138 for (value, pos) in values {
1139 all_fields.push((tag.clone(), value.clone(), *pos));
1140 }
1141 }
1142 all_fields.sort_by_key(|(_, _, pos)| *pos);
1143
1144 #[cfg(debug_assertions)]
1145 eprintln!(
1146 "DEBUG parse_sequence_b_items: found {} total fields to process",
1147 all_fields.len()
1148 );
1149
1150 let message_type = std::any::type_name::<T>();
1152 let sequence_start_tag = if message_type.contains("MT204Transaction") {
1153 "20" } else {
1155 "21" };
1157 let mut current_sequence_fields: HashMap<String, Vec<(String, usize)>> = HashMap::new();
1158 let mut in_sequence = false;
1159
1160 for (tag, value, pos) in all_fields {
1161 if tag == sequence_start_tag
1163 && !tag.ends_with("R")
1164 && !tag.ends_with("F")
1165 && !tag.ends_with("C")
1166 && !tag.ends_with("D")
1167 {
1168 #[cfg(debug_assertions)]
1169 eprintln!(
1170 "DEBUG: Found sequence start tag {} at position {}, in_sequence={}, current_fields_count={}",
1171 tag,
1172 pos,
1173 in_sequence,
1174 current_sequence_fields.len()
1175 );
1176
1177 if in_sequence && !current_sequence_fields.is_empty() {
1179 #[cfg(debug_assertions)]
1180 eprintln!(
1181 "DEBUG: Parsing previous sequence with {} field types",
1182 current_sequence_fields.len()
1183 );
1184
1185 let block4_str = reconstruct_block4_from_fields(¤t_sequence_fields);
1186 match T::parse_from_block4(&block4_str) {
1187 Ok(sequence_item) => {
1188 sequences.push(sequence_item);
1189 #[cfg(debug_assertions)]
1190 eprintln!("DEBUG: Successfully parsed sequence #{}", sequences.len());
1191 }
1192 Err(e) => {
1193 #[cfg(debug_assertions)]
1194 eprintln!("DEBUG: Failed to parse sequence: {}", e);
1195 }
1196 }
1197 current_sequence_fields.clear();
1198 }
1199 in_sequence = true;
1200 }
1201
1202 if in_sequence {
1204 #[cfg(debug_assertions)]
1205 if tag.starts_with("50") {
1206 eprintln!(
1207 "DEBUG: Adding field to sequence: tag='{}', value_start='{}'",
1208 tag,
1209 value.lines().next().unwrap_or("")
1210 );
1211 }
1212 current_sequence_fields
1213 .entry(tag.clone())
1214 .or_default()
1215 .push((value, pos));
1216
1217 tracker.mark_consumed(&tag, pos);
1219 }
1220 }
1221
1222 if in_sequence && !current_sequence_fields.is_empty() {
1224 #[cfg(debug_assertions)]
1225 eprintln!(
1226 "DEBUG: Parsing final sequence with {} field types",
1227 current_sequence_fields.len()
1228 );
1229
1230 let block4_str = reconstruct_block4_from_fields(¤t_sequence_fields);
1231 match T::parse_from_block4(&block4_str) {
1232 Ok(sequence_item) => {
1233 sequences.push(sequence_item);
1234 #[cfg(debug_assertions)]
1235 eprintln!(
1236 "DEBUG: Successfully parsed final sequence #{}",
1237 sequences.len()
1238 );
1239 }
1240 Err(_e) => {
1241 #[cfg(debug_assertions)]
1242 eprintln!("DEBUG: Failed to parse final sequence item: {_e:?}");
1243 }
1244 }
1245 }
1246
1247 #[cfg(debug_assertions)]
1248 eprintln!(
1249 "DEBUG: parse_sequence_b_items returning {} sequences",
1250 sequences.len()
1251 );
1252
1253 Ok(sequences)
1254}