swift_mt_message/parser/
parser_impl.rs

1//! # SWIFT MT Message Parser
2//!
3//! ## Purpose
4//! Comprehensive parser for SWIFT MT (Message Type) messages that converts raw SWIFT message strings
5//! into typed, structured data with full validation and field consumption tracking.
6//!
7//! ## Features
8//! - **Multi-Format Support**: Handles all supported MT message types (101, 103, 104, etc.)
9//! - **Block Structure Parsing**: Extracts and validates all 5 SWIFT message blocks
10//! - **Field Consumption Tracking**: Sequential processing of duplicate fields with position tracking
11//! - **Type-Safe Parsing**: Converts raw strings to strongly-typed field structures
12//! - **Automatic Message Detection**: Auto-detects message type from application header
13//! - **Comprehensive Validation**: Validates message structure, field formats, and business rules
14//!
15//! ## Architecture
16//! The parser follows a layered approach:
17//! 1. **Block Extraction**: Extracts blocks 1-5 from raw SWIFT message
18//! 2. **Header Parsing**: Parses blocks 1, 2, 3, and 5 into header structures
19//! 3. **Field Parsing**: Parses block 4 fields with position and variant tracking
20//! 4. **Message Construction**: Builds typed message structures with sequential field consumption
21//! 5. **Validation**: Applies format and business rule validation
22//!
23//! ## Usage Examples
24//! ```rust
25//! use swift_mt_message::parser::SwiftParser;
26//! use swift_mt_message::messages::MT103;
27//! use swift_mt_message::ParsedSwiftMessage;
28//!
29//! # fn main() -> swift_mt_message::Result<()> {
30//! # let swift_message_string = "{1:F01BANKDEFFAXXX0123456789}{2:I103BANKDEFFAXXXU3003}{4:\n:20:TXN123456\n:23B:CRED\n:32A:240315USD1000,00\n:50K:JOHN DOE\n123 MAIN ST\n:59:DE89370400440532013000\nBENEFICIARY NAME\n:71A:SHA\n-}";
31//! // Parse specific message type
32//! let mt103 = SwiftParser::parse::<MT103>(&swift_message_string)?;
33//!
34//! // Auto-detect message type
35//! let parsed_message = SwiftParser::parse_auto(&swift_message_string)?;
36//! match parsed_message {
37//!     ParsedSwiftMessage::MT103(msg) => println!("Parsed MT103: {:?}", msg),
38//!     ParsedSwiftMessage::MT202(msg) => println!("Parsed MT202: {:?}", msg),
39//!     _ => println!("Other message type"),
40//! }
41//! # Ok(())
42//! # }
43//! ```
44
45use 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
56/// Type alias for the field parsing result with position tracking
57///
58/// Maps field tags to vectors of (field_value, position_in_message) tuples.
59/// This enables sequential consumption of duplicate fields while maintaining message order.
60type FieldParseResult = Result<HashMap<String, Vec<(String, usize)>>>;
61
62/// Parsing context that flows through the parsing pipeline
63#[derive(Debug, Clone)]
64pub struct ParsingContext {
65    /// Current field being parsed
66    pub current_field: Option<String>,
67    /// Current component being parsed
68    pub current_component: Option<String>,
69    /// Message type
70    pub message_type: String,
71    /// Original message for context
72    pub original_message: String,
73}
74
75impl ParsingContext {
76    /// Create a new parsing context
77    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    /// Create a context with field information
87    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    /// Create a context with component information
95    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/// Field consumption tracker for sequential processing of duplicate fields
103///
104/// ## Purpose
105/// Ensures that when a message contains multiple instances of the same field (e.g., multiple :50: fields),
106/// they are consumed sequentially in the order they appear in the original message. This is critical
107/// for messages like MT101 where sequence matters.
108///
109/// ## Implementation
110/// - Tracks consumed field indices by tag
111/// - Provides next available field value for sequential consumption
112/// - Maintains message order integrity during field processing
113///
114/// ## Example
115/// ```rust
116/// use swift_mt_message::parser::FieldConsumptionTracker;
117///
118/// let mut tracker = FieldConsumptionTracker::new();
119/// // Field "50" has values at positions [5, 15, 25] in message
120/// let field_values = vec![
121///     ("value1".to_string(), 5),
122///     ("value2".to_string(), 15),
123///     ("value3".to_string(), 25),
124/// ];
125/// let (value1, pos1) = tracker.get_next_available("50", &field_values).unwrap();
126/// tracker.mark_consumed("50", pos1);
127/// let (value2, pos2) = tracker.get_next_available("50", &field_values).unwrap();
128/// // Ensures value2 is from position 15, not 5 or 25
129/// ```
130#[derive(Debug, Clone)]
131pub struct FieldConsumptionTracker {
132    /// Maps field tags to sets of consumed position indices
133    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    /// Create a new consumption tracker
144    pub fn new() -> Self {
145        Self {
146            consumed_indices: HashMap::new(),
147        }
148    }
149
150    /// Mark a field value at a specific position as consumed
151    pub fn mark_consumed(&mut self, tag: &str, index: usize) {
152        // Avoid allocation when the key already exists
153        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    /// Get the next available (unconsumed) field value for a tag
167    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        // Find first unconsumed value in original message order
175        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
182/// Find field values by base tag with sequential consumption tracking and variant constraints
183///
184/// ## Purpose
185/// Enhanced version of find_field_with_variant_sequential that accepts a list of valid variants
186/// to constrain the search. This is crucial for numbered field tags like "50#1" and "50#2"
187/// where different positions accept different variants.
188///
189/// ## Parameters
190/// - `fields`: HashMap of all parsed fields with position tracking
191/// - `base_tag`: Base field tag (e.g., "50", "59")
192/// - `tracker`: Mutable reference to consumption tracker for sequential processing
193/// - `valid_variants`: Optional list of valid variant letters (e.g., ["C", "L"] for Field50InstructingParty)
194///
195/// ## Returns
196/// `Option<(field_value, variant, position)>` where:
197/// - `field_value`: The actual field content
198/// - `variant`: Optional variant letter (A, F, K, etc.) for enum fields
199/// - `position`: Original position in the message for ordering
200pub 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    // First try to find exact match (for non-variant fields)
207    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    // For enum fields, look for variant tags (50A, 50F, 50K, etc.)
215    // Sort tags by position to ensure we process them in order
216    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    // Sort by the minimum unconsumed position in each tag's values
229    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        // Check if this variant is allowed (if constraints are provided)
248        if let Some(valid) = valid_variants {
249            if !valid.contains(&variant_str.as_str()) {
250                continue; // Skip variants that aren't in the valid list
251            }
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
263/// Main parser for SWIFT MT messages
264///
265/// ## Purpose
266/// Primary parsing engine that converts raw SWIFT message strings into typed, validated message structures.
267/// Handles all aspects of SWIFT message processing including block extraction, header parsing, field parsing,
268/// and type-safe message construction.
269///
270/// ## Capabilities
271/// - **Multi-Message Support**: Parses all 24 supported MT message types
272/// - **Flexible Parsing**: Both type-specific and auto-detection parsing modes
273/// - **Robust Error Handling**: Comprehensive error reporting for malformed messages
274/// - **Field Validation**: Format validation and business rule checking
275/// - **Position Tracking**: Maintains field order for sequential processing requirements
276///
277/// ## Parsing Process
278/// 1. **Block Extraction**: Identifies and extracts SWIFT blocks 1-5
279/// 2. **Header Validation**: Parses and validates basic, application, user headers and trailer
280/// 3. **Message Type Detection**: Determines message type from application header
281/// 4. **Field Processing**: Parses block 4 fields with position and variant tracking
282/// 5. **Type Construction**: Builds strongly-typed message structures
283/// 6. **Validation**: Applies format and business rule validation
284///
285/// ## Thread Safety
286/// SwiftParser is stateless and thread-safe. All methods are static and can be called
287/// concurrently from multiple threads.
288pub struct SwiftParser {
289    config: ParserConfig,
290}
291
292impl Default for SwiftParser {
293    fn default() -> Self {
294        Self::new()
295    }
296}
297
298impl SwiftParser {
299    /// Create a new parser with default configuration
300    pub fn new() -> Self {
301        Self {
302            config: ParserConfig::default(),
303        }
304    }
305
306    /// Create a new parser with custom configuration
307    pub fn with_config(config: ParserConfig) -> Self {
308        Self { config }
309    }
310
311    /// Parse a message and return ParseResult with all errors collected
312    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        // Parse headers
323        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        // Extract message type from application header
329        let message_type = application_header.message_type.clone();
330
331        // Validate message type matches expected type using SWIFT error codes
332        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        // Parse block 4 fields with position tracking
349        let field_map_with_positions = Self::parse_block4_fields(&block4.unwrap_or_default())?;
350
351        // Use configuration-aware parsing
352        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    /// Parse a raw SWIFT message string into a typed message (static method for backward compatibility)
384    pub fn parse<T: SwiftMessageBody>(raw_message: &str) -> Result<SwiftMessage<T>> {
385        Self::new().parse_message(raw_message)
386    }
387
388    /// Parse a raw SWIFT message string into a typed message with configuration support
389    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        // Parse headers
397        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        // Extract message type from application header
403        let message_type = application_header.message_type.clone();
404
405        // Validate message type matches expected type using SWIFT error codes
406        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        // Parse block 4 fields with position tracking
423        let field_map_with_positions = Self::parse_block4_fields(&block4.unwrap_or_default())?;
424
425        // Use configuration-aware parsing
426        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                // For partial success, we could log the errors but still return the message
439                // For now, we'll return the message but the errors are available in the ParseResult
440                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                // Convert to MultipleErrors
455                Err(ParseError::MultipleErrors(errors))
456            }
457        }
458    }
459
460    /// Parse a raw SWIFT message string with automatic message type detection (static method for backward compatibility)
461    pub fn parse_auto(raw_message: &str) -> Result<ParsedSwiftMessage> {
462        Self::new().parse_message_auto(raw_message)
463    }
464
465    /// Parse a raw SWIFT message string with automatic message type detection and configuration support
466    pub fn parse_message_auto(&self, raw_message: &str) -> Result<ParsedSwiftMessage> {
467        // First, extract blocks to get the message type
468        let block2 = Self::extract_block(raw_message, 2)?;
469
470        // Parse application header to get message type
471        let application_header = ApplicationHeader::parse(&block2.unwrap_or_default())?;
472        let message_type = &application_header.message_type;
473
474        // Route to appropriate parser based on message type
475        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    /// Extract a specific message block from raw SWIFT message with SWIFT validation
579    pub fn extract_block(raw_message: &str, block_index: u8) -> Result<Option<String>> {
580        // Validate block index using SWIFT error codes
581        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                    // Blocks 1 and 2 end with simple closing brace (no nested content)
601                    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                    // Blocks 3 and 5 may have nested braces (e.g., {103:EBA} or {CHK:...})
610                    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                    // Block 4 ends with "-}"
619                    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    /// Parse block 4 fields into a field map with enhanced position tracking
642    fn parse_block4_fields(block4: &str) -> FieldParseResult {
643        // Use the generated parser function from the generated module
644        crate::parser::parse_block4_fields(block4)
645    }
646
647    /// Find the matching closing brace for a block that starts with an opening brace
648    /// Handles nested braces correctly
649    fn find_matching_brace(text: &str) -> Option<usize> {
650        let mut chars = text.char_indices();
651
652        // Skip the first character (should be '{')
653        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
676/// Parse a SwiftMessage from a string representation
677/// This is a placeholder implementation for the macro system
678pub fn parse_swift_message_from_string(value: &str) -> Result<HashMap<String, Vec<String>>> {
679    // For now, this is a stub implementation
680    // In a real implementation, this would parse the string representation
681    // of a SwiftMessage back into a field map
682
683    // As a temporary solution, we'll assume the value is a simple field representation
684    // and try to parse it as a mini SWIFT block
685    let mut field_map = HashMap::new();
686
687    // Split by lines and parse each field
688    for line in value.lines() {
689        if line.trim().is_empty() {
690            continue;
691        }
692
693        // Look for field pattern :XX:value
694        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
711/// Parse sequence fields (e.g., transactions in MT101, MT104)
712///
713/// This function identifies sequence boundaries and parses each sequence into the target type.
714/// Sequences typically start with a mandatory field (like Field 21) that marks the beginning
715/// of each repetition.
716pub 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    // Use the enhanced sequence parser for messages that have complex sequences
724    let message_type = std::any::type_name::<T>();
725
726    if message_type.contains("MT104Transaction") {
727        // For MT104, we need to handle the three-sequence structure
728        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        // Parse only sequence B (transactions)
734        return parse_sequence_b_items::<T>(&parsed_sequences.sequence_b, tracker);
735    }
736
737    // Get all fields sorted by position
738    let mut all_fields: Vec<(String, String, usize)> = Vec::new();
739    for (tag, values) in fields {
740        for (value, pos) in values {
741            // Only include unconsumed fields
742            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    // Determine the sequence start marker based on message type
754    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        // Check if this is the start of a new sequence
772        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 we were already in a sequence, parse the previous one
781            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        // Add field to current sequence if we're in one
791        if in_sequence {
792            current_sequence_fields
793                .entry(tag.clone())
794                .or_default()
795                .push((value, pos));
796
797            // Mark this field as consumed
798            tracker.mark_consumed(&tag, pos);
799        }
800    }
801
802    // Parse the last sequence if there is one
803    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
818/// Parse sequence B items from already split fields
819fn 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    // Get all fields sorted by position
829    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        // Check if this is the start of a new sequence
843        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 we were already in a sequence, parse the previous one
850            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        // Add field to current sequence if we're in one
860        if in_sequence {
861            current_sequence_fields
862                .entry(tag.clone())
863                .or_default()
864                .push((value, pos));
865
866            // Mark this field as consumed
867            tracker.mark_consumed(&tag, pos);
868        }
869    }
870
871    // Parse the last sequence if there is one
872    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
887/// Serialize a SwiftMessage field map to a string representation
888/// This is a placeholder implementation for the macro system
889pub fn serialize_swift_message_to_string(fields: &HashMap<String, Vec<String>>) -> String {
890    // For now, this is a stub implementation
891    // In a real implementation, this would serialize the field map
892    // into a string representation of a SwiftMessage
893
894    let mut result = String::new();
895
896    // Simple serialization: just join all field values with newlines
897    for field_values in fields.values() {
898        for field_value in field_values {
899            // field_value should already be in the format ":XX:value"
900            result.push_str(field_value);
901            result.push('\n');
902        }
903    }
904
905    // Remove trailing newline
906    if result.ends_with('\n') {
907        result.pop();
908    }
909
910    result
911}