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, 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/// Type alias for the field parsing result with position tracking
58///
59/// Maps field tags to vectors of (field_value, position_in_message) tuples.
60/// This enables sequential consumption of duplicate fields while maintaining message order.
61type FieldParseResult = Result<HashMap<String, Vec<(String, usize)>>>;
62
63/// Parsing context that flows through the parsing pipeline
64#[derive(Debug, Clone)]
65pub struct ParsingContext {
66    /// Current field being parsed
67    pub current_field: Option<String>,
68    /// Current component being parsed
69    pub current_component: Option<String>,
70    /// Message type
71    pub message_type: String,
72    /// Original message for context
73    pub original_message: String,
74}
75
76impl ParsingContext {
77    /// Create a new parsing context
78    pub fn new(message_type: String, original_message: String) -> Self {
79        Self {
80            current_field: None,
81            current_component: None,
82            message_type,
83            original_message,
84        }
85    }
86
87    /// Create a context with field information
88    pub fn with_field(&self, field: String) -> Self {
89        let mut ctx = self.clone();
90        ctx.current_field = Some(field);
91        ctx.current_component = None;
92        ctx
93    }
94
95    /// Create a context with component information
96    pub fn with_component(&self, component: String) -> Self {
97        let mut ctx = self.clone();
98        ctx.current_component = Some(component);
99        ctx
100    }
101}
102
103/// Field consumption tracker for sequential processing of duplicate fields
104///
105/// ## Purpose
106/// Ensures that when a message contains multiple instances of the same field (e.g., multiple :50: fields),
107/// they are consumed sequentially in the order they appear in the original message. This is critical
108/// for messages like MT101 where sequence matters.
109///
110/// ## Implementation
111/// - Tracks consumed field indices by tag
112/// - Provides next available field value for sequential consumption
113/// - Maintains message order integrity during field processing
114///
115/// ## Example
116/// ```rust
117/// use swift_mt_message::parser::FieldConsumptionTracker;
118///
119/// let mut tracker = FieldConsumptionTracker::new();
120/// // Field "50" has values at positions [5, 15, 25] in message
121/// let field_values = vec![
122///     ("value1".to_string(), 5),
123///     ("value2".to_string(), 15),
124///     ("value3".to_string(), 25),
125/// ];
126/// let (value1, pos1) = tracker.get_next_available("50", &field_values).unwrap();
127/// tracker.mark_consumed("50", pos1);
128/// let (value2, pos2) = tracker.get_next_available("50", &field_values).unwrap();
129/// // Ensures value2 is from position 15, not 5 or 25
130/// ```
131#[derive(Debug, Clone)]
132pub struct FieldConsumptionTracker {
133    /// Maps field tags to sets of consumed position indices
134    consumed_indices: HashMap<String, HashSet<usize>>,
135}
136
137impl Default for FieldConsumptionTracker {
138    fn default() -> Self {
139        Self::new()
140    }
141}
142
143impl FieldConsumptionTracker {
144    /// Create a new consumption tracker
145    pub fn new() -> Self {
146        Self {
147            consumed_indices: HashMap::new(),
148        }
149    }
150
151    /// Mark a field value at a specific position as consumed
152    pub fn mark_consumed(&mut self, tag: &str, index: usize) {
153        // Avoid allocation when the key already exists
154        use std::collections::hash_map::Entry;
155        match self.consumed_indices.entry(tag.to_string()) {
156            Entry::Occupied(mut e) => {
157                e.get_mut().insert(index);
158            }
159            Entry::Vacant(e) => {
160                let mut set = HashSet::new();
161                set.insert(index);
162                e.insert(set);
163            }
164        }
165    }
166
167    /// Get the next available (unconsumed) field value for a tag
168    pub fn get_next_available<'a>(
169        &self,
170        tag: &str,
171        values: &'a [(String, usize)],
172    ) -> Option<(&'a str, usize)> {
173        let consumed_set = self.consumed_indices.get(tag);
174
175        // Find first unconsumed value in original message order
176        values
177            .iter()
178            .find(|(_, pos)| consumed_set.is_none_or(|set| !set.contains(pos)))
179            .map(|(value, pos)| (value.as_str(), *pos))
180    }
181}
182
183/// Find field values by base tag with sequential consumption tracking and variant constraints
184///
185/// ## Purpose
186/// Enhanced version of find_field_with_variant_sequential that accepts a list of valid variants
187/// to constrain the search. This is crucial for numbered field tags like "50#1" and "50#2"
188/// where different positions accept different variants.
189///
190/// ## Parameters
191/// - `fields`: HashMap of all parsed fields with position tracking
192/// - `base_tag`: Base field tag (e.g., "50", "59")
193/// - `tracker`: Mutable reference to consumption tracker for sequential processing
194/// - `valid_variants`: Optional list of valid variant letters (e.g., ["C", "L"] for Field50InstructingParty)
195///
196/// ## Returns
197/// `Option<(field_value, variant, position)>` where:
198/// - `field_value`: The actual field content
199/// - `variant`: Optional variant letter (A, F, K, etc.) for enum fields
200/// - `position`: Original position in the message for ordering
201pub fn find_field_with_variant_sequential_constrained(
202    fields: &HashMap<String, Vec<(String, usize)>>,
203    base_tag: &str,
204    tracker: &mut FieldConsumptionTracker,
205    valid_variants: Option<&[&str]>,
206) -> Option<(String, Option<String>, usize)> {
207    // First try to find exact match (for non-variant fields)
208    if let Some(values) = fields.get(base_tag)
209        && let Some((value, pos)) = tracker.get_next_available(base_tag, values)
210    {
211        tracker.mark_consumed(base_tag, pos);
212        return Some((value.to_string(), None, pos));
213    }
214
215    // For enum fields, look for variant tags (50A, 50F, 50K, etc.)
216    // Sort tags by position to ensure we process them in order
217    let mut variant_candidates: Vec<(&String, &Vec<(String, usize)>)> = fields
218        .iter()
219        .filter(|(tag, _)| {
220            tag.starts_with(base_tag)
221                && tag.len() == base_tag.len() + 1
222                && tag
223                    .chars()
224                    .last()
225                    .is_some_and(|c| c.is_ascii_alphabetic() && c.is_ascii_uppercase())
226        })
227        .collect();
228
229    // Sort by the minimum unconsumed position in each tag's values
230    variant_candidates.sort_by_key(|(tag, values)| {
231        values
232            .iter()
233            .filter(|(_, pos)| {
234                tracker
235                    .consumed_indices
236                    .get(*tag)
237                    .is_none_or(|set| !set.contains(pos))
238            })
239            .map(|(_, pos)| *pos)
240            .min()
241            .unwrap_or(usize::MAX)
242    });
243
244    for (tag, values) in variant_candidates {
245        let variant_char = tag.chars().last().unwrap();
246        let variant_str = variant_char.to_string();
247
248        // Check if this variant is allowed (if constraints are provided)
249        if let Some(valid) = valid_variants
250            && !valid.contains(&variant_str.as_str())
251        {
252            continue; // Skip variants that aren't in the valid list
253        }
254
255        if let Some((value, pos)) = tracker.get_next_available(tag, values) {
256            tracker.mark_consumed(tag, pos);
257            return Some((value.to_string(), Some(variant_str), pos));
258        }
259    }
260
261    None
262}
263
264/// Main parser for SWIFT MT messages
265///
266/// ## Purpose
267/// Primary parsing engine that converts raw SWIFT message strings into typed, validated message structures.
268/// Handles all aspects of SWIFT message processing including block extraction, header parsing, field parsing,
269/// and type-safe message construction.
270///
271/// ## Capabilities
272/// - **Multi-Message Support**: Parses all 24 supported MT message types
273/// - **Flexible Parsing**: Both type-specific and auto-detection parsing modes
274/// - **Robust Error Handling**: Comprehensive error reporting for malformed messages
275/// - **Field Validation**: Format validation and business rule checking
276/// - **Position Tracking**: Maintains field order for sequential processing requirements
277///
278/// ## Parsing Process
279/// 1. **Block Extraction**: Identifies and extracts SWIFT blocks 1-5
280/// 2. **Header Validation**: Parses and validates basic, application, user headers and trailer
281/// 3. **Message Type Detection**: Determines message type from application header
282/// 4. **Field Processing**: Parses block 4 fields with position and variant tracking
283/// 5. **Type Construction**: Builds strongly-typed message structures
284/// 6. **Validation**: Applies format and business rule validation
285///
286/// ## Thread Safety
287/// SwiftParser is stateless and thread-safe. All methods are static and can be called
288/// concurrently from multiple threads.
289pub struct SwiftParser {
290    config: ParserConfig,
291}
292
293impl Default for SwiftParser {
294    fn default() -> Self {
295        Self::new()
296    }
297}
298
299impl SwiftParser {
300    /// Create a new parser with default configuration
301    pub fn new() -> Self {
302        Self {
303            config: ParserConfig::default(),
304        }
305    }
306
307    /// Create a new parser with custom configuration
308    pub fn with_config(config: ParserConfig) -> Self {
309        Self { config }
310    }
311
312    /// Parse a message and return ParseResult with all errors collected
313    pub fn parse_with_errors<T: SwiftMessageBody>(
314        &self,
315        raw_message: &str,
316    ) -> Result<crate::errors::ParseResult<SwiftMessage<T>>> {
317        let block1 = Self::extract_block(raw_message, 1)?;
318        let block2 = Self::extract_block(raw_message, 2)?;
319        let block3 = Self::extract_block(raw_message, 3)?;
320        let block4 = Self::extract_block(raw_message, 4)?;
321        let block5 = Self::extract_block(raw_message, 5)?;
322
323        // Parse headers
324        let basic_header = BasicHeader::parse(&block1.unwrap_or_default())?;
325        let application_header = ApplicationHeader::parse(&block2.unwrap_or_default())?;
326        let user_header = block3.map(|b| UserHeader::parse(&b)).transpose()?;
327        let trailer = block5.map(|b| Trailer::parse(&b)).transpose()?;
328
329        // Extract message type from application header
330        let message_type = application_header.message_type.clone();
331
332        // Validate message type matches expected type using SWIFT error codes
333        if message_type != T::message_type() {
334            return Err(ParseError::SwiftValidation(Box::new(
335                SwiftValidationError::format_error(
336                    t_series::T03,
337                    "MESSAGE_TYPE",
338                    &message_type,
339                    T::message_type(),
340                    &format!(
341                        "Message type mismatch: expected {}, got {}",
342                        T::message_type(),
343                        message_type
344                    ),
345                ),
346            )));
347        }
348
349        // Parse block 4 fields with position tracking
350        let field_map_with_positions = Self::parse_block4_fields(&block4.unwrap_or_default())?;
351
352        // Use configuration-aware parsing
353        let parse_result = T::from_fields_with_config(field_map_with_positions, &self.config)?;
354
355        match parse_result {
356            crate::errors::ParseResult::Success(fields) => {
357                Ok(crate::errors::ParseResult::Success(SwiftMessage {
358                    basic_header,
359                    application_header,
360                    user_header,
361                    trailer,
362                    message_type,
363                    fields,
364                }))
365            }
366            crate::errors::ParseResult::PartialSuccess(fields, errors) => {
367                Ok(crate::errors::ParseResult::PartialSuccess(
368                    SwiftMessage {
369                        basic_header,
370                        application_header,
371                        user_header,
372                        trailer,
373                        message_type,
374                        fields,
375                    },
376                    errors,
377                ))
378            }
379            crate::errors::ParseResult::Failure(errors) => {
380                Ok(crate::errors::ParseResult::Failure(errors))
381            }
382        }
383    }
384    /// Parse a raw SWIFT message string into a typed message (static method for backward compatibility)
385    pub fn parse<T: SwiftMessageBody>(raw_message: &str) -> Result<SwiftMessage<T>> {
386        Self::new().parse_message(raw_message)
387    }
388
389    /// Parse a raw SWIFT message string into a typed message with configuration support
390    pub fn parse_message<T: SwiftMessageBody>(&self, raw_message: &str) -> Result<SwiftMessage<T>> {
391        let block1 = Self::extract_block(raw_message, 1)?;
392        let block2 = Self::extract_block(raw_message, 2)?;
393        let block3 = Self::extract_block(raw_message, 3)?;
394        let block4 = Self::extract_block(raw_message, 4)?;
395        let block5 = Self::extract_block(raw_message, 5)?;
396
397        // Parse headers
398        let basic_header = BasicHeader::parse(&block1.unwrap_or_default())?;
399        let application_header = ApplicationHeader::parse(&block2.unwrap_or_default())?;
400        let user_header = block3.map(|b| UserHeader::parse(&b)).transpose()?;
401        let trailer = block5.map(|b| Trailer::parse(&b)).transpose()?;
402
403        // Extract message type from application header
404        let message_type = application_header.message_type.clone();
405
406        // Validate message type matches expected type using SWIFT error codes
407        if message_type != T::message_type() {
408            return Err(ParseError::SwiftValidation(Box::new(
409                SwiftValidationError::format_error(
410                    t_series::T03,
411                    "MESSAGE_TYPE",
412                    &message_type,
413                    T::message_type(),
414                    &format!(
415                        "Message type mismatch: expected {}, got {}",
416                        T::message_type(),
417                        message_type
418                    ),
419                ),
420            )));
421        }
422
423        // Parse block 4 fields with position tracking
424        let field_map_with_positions = Self::parse_block4_fields(&block4.unwrap_or_default())?;
425
426        // Use configuration-aware parsing
427        let parse_result = T::from_fields_with_config(field_map_with_positions, &self.config)?;
428
429        match parse_result {
430            crate::errors::ParseResult::Success(fields) => Ok(SwiftMessage {
431                basic_header,
432                application_header,
433                user_header,
434                trailer,
435                message_type,
436                fields,
437            }),
438            crate::errors::ParseResult::PartialSuccess(fields, errors) => {
439                // For partial success, we could log the errors but still return the message
440                // For now, we'll return the message but the errors are available in the ParseResult
441                eprintln!("Warning: Parsed with {} non-critical errors", errors.len());
442                for error in &errors {
443                    eprintln!("  - {error}");
444                }
445                Ok(SwiftMessage {
446                    basic_header,
447                    application_header,
448                    user_header,
449                    trailer,
450                    message_type,
451                    fields,
452                })
453            }
454            crate::errors::ParseResult::Failure(errors) => {
455                // Convert to MultipleErrors
456                Err(ParseError::MultipleErrors(errors))
457            }
458        }
459    }
460
461    /// Parse a raw SWIFT message string with automatic message type detection (static method for backward compatibility)
462    pub fn parse_auto(raw_message: &str) -> Result<ParsedSwiftMessage> {
463        Self::new().parse_message_auto(raw_message)
464    }
465
466    /// Parse a raw SWIFT message string with automatic message type detection and configuration support
467    pub fn parse_message_auto(&self, raw_message: &str) -> Result<ParsedSwiftMessage> {
468        // First, extract blocks to get the message type
469        let block2 = Self::extract_block(raw_message, 2)?;
470
471        // Parse application header to get message type
472        let application_header = ApplicationHeader::parse(&block2.unwrap_or_default())?;
473        let message_type = &application_header.message_type;
474
475        // Route to appropriate parser based on message type
476        match message_type.as_str() {
477            "101" => {
478                let parsed = self.parse_message::<MT101>(raw_message)?;
479                Ok(ParsedSwiftMessage::MT101(Box::new(parsed)))
480            }
481            "103" => {
482                let parsed = self.parse_message::<MT103>(raw_message)?;
483                Ok(ParsedSwiftMessage::MT103(Box::new(parsed)))
484            }
485            "104" => {
486                let parsed = self.parse_message::<MT104>(raw_message)?;
487                Ok(ParsedSwiftMessage::MT104(Box::new(parsed)))
488            }
489            "107" => {
490                let parsed = self.parse_message::<MT107>(raw_message)?;
491                Ok(ParsedSwiftMessage::MT107(Box::new(parsed)))
492            }
493            "110" => {
494                let parsed = self.parse_message::<MT110>(raw_message)?;
495                Ok(ParsedSwiftMessage::MT110(Box::new(parsed)))
496            }
497            "111" => {
498                let parsed = self.parse_message::<MT111>(raw_message)?;
499                Ok(ParsedSwiftMessage::MT111(Box::new(parsed)))
500            }
501            "112" => {
502                let parsed = self.parse_message::<MT112>(raw_message)?;
503                Ok(ParsedSwiftMessage::MT112(Box::new(parsed)))
504            }
505            "190" => {
506                let parsed = self.parse_message::<MT190>(raw_message)?;
507                Ok(ParsedSwiftMessage::MT190(Box::new(parsed)))
508            }
509            "191" => {
510                let parsed = self.parse_message::<MT191>(raw_message)?;
511                Ok(ParsedSwiftMessage::MT191(Box::new(parsed)))
512            }
513            "200" => {
514                let parsed = self.parse_message::<MT200>(raw_message)?;
515                Ok(ParsedSwiftMessage::MT200(Box::new(parsed)))
516            }
517            "202" => {
518                let parsed = self.parse_message::<MT202>(raw_message)?;
519                Ok(ParsedSwiftMessage::MT202(Box::new(parsed)))
520            }
521            "204" => {
522                let parsed = self.parse_message::<MT204>(raw_message)?;
523                Ok(ParsedSwiftMessage::MT204(Box::new(parsed)))
524            }
525            "205" => {
526                let parsed = self.parse_message::<MT205>(raw_message)?;
527                Ok(ParsedSwiftMessage::MT205(Box::new(parsed)))
528            }
529            "210" => {
530                let parsed = self.parse_message::<MT210>(raw_message)?;
531                Ok(ParsedSwiftMessage::MT210(Box::new(parsed)))
532            }
533            "290" => {
534                let parsed = self.parse_message::<MT290>(raw_message)?;
535                Ok(ParsedSwiftMessage::MT290(Box::new(parsed)))
536            }
537            "291" => {
538                let parsed = self.parse_message::<MT291>(raw_message)?;
539                Ok(ParsedSwiftMessage::MT291(Box::new(parsed)))
540            }
541            "900" => {
542                let parsed = self.parse_message::<MT900>(raw_message)?;
543                Ok(ParsedSwiftMessage::MT900(Box::new(parsed)))
544            }
545            "910" => {
546                let parsed = self.parse_message::<MT910>(raw_message)?;
547                Ok(ParsedSwiftMessage::MT910(Box::new(parsed)))
548            }
549            "920" => {
550                let parsed = self.parse_message::<MT920>(raw_message)?;
551                Ok(ParsedSwiftMessage::MT920(Box::new(parsed)))
552            }
553            "935" => {
554                let parsed = self.parse_message::<MT935>(raw_message)?;
555                Ok(ParsedSwiftMessage::MT935(Box::new(parsed)))
556            }
557            "940" => {
558                let parsed = self.parse_message::<MT940>(raw_message)?;
559                Ok(ParsedSwiftMessage::MT940(Box::new(parsed)))
560            }
561            "941" => {
562                let parsed = self.parse_message::<MT941>(raw_message)?;
563                Ok(ParsedSwiftMessage::MT941(Box::new(parsed)))
564            }
565            "942" => {
566                let parsed = self.parse_message::<MT942>(raw_message)?;
567                Ok(ParsedSwiftMessage::MT942(Box::new(parsed)))
568            }
569            "950" => {
570                let parsed = self.parse_message::<MT950>(raw_message)?;
571                Ok(ParsedSwiftMessage::MT950(Box::new(parsed)))
572            }
573            "192" => {
574                let parsed = self.parse_message::<MT192>(raw_message)?;
575                Ok(ParsedSwiftMessage::MT192(Box::new(parsed)))
576            }
577            "196" => {
578                let parsed = self.parse_message::<MT196>(raw_message)?;
579                Ok(ParsedSwiftMessage::MT196(Box::new(parsed)))
580            }
581            "292" => {
582                let parsed = self.parse_message::<MT292>(raw_message)?;
583                Ok(ParsedSwiftMessage::MT292(Box::new(parsed)))
584            }
585            "296" => {
586                let parsed = self.parse_message::<MT296>(raw_message)?;
587                Ok(ParsedSwiftMessage::MT296(Box::new(parsed)))
588            }
589            "199" => {
590                let parsed = self.parse_message::<MT199>(raw_message)?;
591                Ok(ParsedSwiftMessage::MT199(Box::new(parsed)))
592            }
593            "299" => {
594                let parsed = self.parse_message::<MT299>(raw_message)?;
595                Ok(ParsedSwiftMessage::MT299(Box::new(parsed)))
596            }
597            _ => Err(ParseError::UnsupportedMessageType {
598                message_type: message_type.clone(),
599            }),
600        }
601    }
602
603    /// Extract a specific message block from raw SWIFT message with SWIFT validation
604    pub fn extract_block(raw_message: &str, block_index: u8) -> Result<Option<String>> {
605        // Validate block index using SWIFT error codes
606        if !(1..=5).contains(&block_index) {
607            return Err(ParseError::SwiftValidation(Box::new(
608                crate::errors::SwiftValidationError::format_error(
609                    crate::swift_error_codes::t_series::T01,
610                    "BLOCK_INDEX",
611                    &block_index.to_string(),
612                    "1-5",
613                    &format!("Invalid block index: {block_index}"),
614                ),
615            )));
616        }
617
618        let block_marker = format!("{{{block_index}:");
619
620        if let Some(start) = raw_message.find(&block_marker) {
621            let content_start = start + block_marker.len();
622
623            match block_index {
624                1 | 2 => {
625                    // Blocks 1 and 2 end with simple closing brace (no nested content)
626                    if let Some(end) = raw_message[start..].find('}') {
627                        let end = start + end;
628                        Ok(Some(raw_message[content_start..end].to_string()))
629                    } else {
630                        Ok(None)
631                    }
632                }
633                3 | 5 => {
634                    // Blocks 3 and 5 may have nested braces (e.g., {103:EBA} or {CHK:...})
635                    if let Some(end) = Self::find_matching_brace(&raw_message[start..]) {
636                        let end = start + end;
637                        Ok(Some(raw_message[content_start..end].to_string()))
638                    } else {
639                        Ok(None)
640                    }
641                }
642                4 => {
643                    // Block 4 ends with "-}"
644                    if let Some(end) = raw_message[start..].find("-}") {
645                        let end = start + end;
646                        Ok(Some(raw_message[content_start..end].to_string()))
647                    } else {
648                        Ok(None)
649                    }
650                }
651                _ => Err(ParseError::SwiftValidation(Box::new(
652                    crate::errors::SwiftValidationError::format_error(
653                        crate::swift_error_codes::t_series::T02,
654                        "BLOCK",
655                        &block_index.to_string(),
656                        "1-5",
657                        &format!("Invalid block index: {block_index}"),
658                    ),
659                ))),
660            }
661        } else {
662            Ok(None)
663        }
664    }
665
666    /// Parse block 4 fields into a field map with enhanced position tracking
667    fn parse_block4_fields(block4: &str) -> FieldParseResult {
668        // Use the generated parser function from the generated module
669        crate::parser::parse_block4_fields(block4)
670    }
671
672    /// Find the matching closing brace for a block that starts with an opening brace
673    /// Handles nested braces correctly
674    fn find_matching_brace(text: &str) -> Option<usize> {
675        let mut chars = text.char_indices();
676
677        // Skip the first character (should be '{')
678        let mut brace_count = if let Some((_, '{')) = chars.next() {
679            1
680        } else {
681            return None;
682        };
683
684        for (i, ch) in chars {
685            match ch {
686                '{' => brace_count += 1,
687                '}' => {
688                    brace_count -= 1;
689                    if brace_count == 0 {
690                        return Some(i);
691                    }
692                }
693                _ => {}
694            }
695        }
696
697        None
698    }
699}
700
701/// Parse a SwiftMessage from a string representation
702/// This is a placeholder implementation for the macro system
703pub fn parse_swift_message_from_string(value: &str) -> Result<HashMap<String, Vec<String>>> {
704    // For now, this is a stub implementation
705    // In a real implementation, this would parse the string representation
706    // of a SwiftMessage back into a field map
707
708    // As a temporary solution, we'll assume the value is a simple field representation
709    // and try to parse it as a mini SWIFT block
710    let mut field_map = HashMap::new();
711
712    // Split by lines and parse each field
713    for line in value.lines() {
714        if line.trim().is_empty() {
715            continue;
716        }
717
718        // Look for field pattern :XX:value
719        if let Some(colon_pos) = line.find(':')
720            && let Some(second_colon) = line[colon_pos + 1..].find(':')
721        {
722            let second_colon_pos = colon_pos + 1 + second_colon;
723            let field_tag = line[colon_pos + 1..second_colon_pos].to_string();
724            let _field_value = line[second_colon_pos + 1..].to_string();
725
726            field_map
727                .entry(field_tag)
728                .or_insert_with(Vec::new)
729                .push(format!(":{}", &line[colon_pos + 1..]));
730        }
731    }
732
733    Ok(field_map)
734}
735
736/// Parse sequence fields (e.g., transactions in MT101, MT104)
737///
738/// This function identifies sequence boundaries and parses each sequence into the target type.
739/// Sequences typically start with a mandatory field (like Field 21) that marks the beginning
740/// of each repetition.
741pub fn parse_sequences<T>(
742    fields: &HashMap<String, Vec<(String, usize)>>,
743    tracker: &mut FieldConsumptionTracker,
744) -> Result<Vec<T>>
745where
746    T: crate::SwiftMessageBody,
747{
748    // Use the enhanced sequence parser for messages that have complex sequences
749    let message_type = std::any::type_name::<T>();
750
751    if message_type.contains("MT104Transaction") {
752        // For MT104, we need to handle the three-sequence structure
753        use crate::parser::sequence_parser::{get_sequence_config, split_into_sequences};
754
755        let config = get_sequence_config("MT104");
756        let parsed_sequences = split_into_sequences(fields, &config)?;
757
758        // Parse only sequence B (transactions)
759        return parse_sequence_b_items::<T>(&parsed_sequences.sequence_b, tracker);
760    }
761
762    if message_type.contains("MT204Transaction") {
763        // MT204 has a special structure where fields are grouped by type, not by transaction
764        // We need to reconstruct transactions from the grouped fields
765
766        // Count how many transactions we have (based on field 20 occurrences, excluding the first one)
767        let field_20_count = fields.get("20").map(|v| v.len()).unwrap_or(0);
768        if field_20_count <= 1 {
769            return Ok(Vec::new()); // No transactions if only one or zero field 20s
770        }
771
772        let num_transactions = field_20_count - 1; // First field 20 is for sequence A
773
774        // Build transactions by distributing fields
775        let mut transactions = Vec::new();
776
777        for i in 0..num_transactions {
778            let mut tx_fields = HashMap::new();
779
780            // Get field 20 (skip the first one which is for sequence A)
781            if let Some(field_20_values) = fields.get("20")
782                && i + 1 < field_20_values.len()
783            {
784                tx_fields.insert("20".to_string(), vec![field_20_values[i + 1].clone()]);
785            }
786
787            // Get field 21 if present (optional)
788            if let Some(field_21_values) = fields.get("21")
789                && i < field_21_values.len()
790            {
791                tx_fields.insert("21".to_string(), vec![field_21_values[i].clone()]);
792            }
793
794            // Get field 32B
795            if let Some(field_32b_values) = fields.get("32B")
796                && i < field_32b_values.len()
797            {
798                tx_fields.insert("32B".to_string(), vec![field_32b_values[i].clone()]);
799            }
800
801            // Get field 53 (various variants)
802            for variant in ["53", "53A", "53B", "53D"] {
803                if let Some(field_53_values) = fields.get(variant)
804                    && i < field_53_values.len()
805                {
806                    tx_fields.insert(variant.to_string(), vec![field_53_values[i].clone()]);
807                    break; // Only one variant per transaction
808                }
809            }
810
811            // Get field 72 if present (optional)
812            if let Some(field_72_values) = fields.get("72") {
813                // Field 72 can appear multiple times, need to figure out which ones belong to transactions
814                // For now, skip the first two (which are for sequence A) and take one per transaction
815                if i + 2 < field_72_values.len() {
816                    tx_fields.insert("72".to_string(), vec![field_72_values[i + 2].clone()]);
817                }
818            }
819
820            // Parse the transaction
821            if let Ok(transaction) = T::from_fields(tx_fields) {
822                transactions.push(transaction);
823            }
824        }
825
826        return Ok(transactions);
827    }
828
829    // Get all fields sorted by position
830    let mut all_fields: Vec<(String, String, usize)> = Vec::new();
831    for (tag, values) in fields {
832        for (value, pos) in values {
833            // Only include unconsumed fields
834            if tracker
835                .consumed_indices
836                .get(tag)
837                .is_none_or(|set| !set.contains(pos))
838            {
839                all_fields.push((tag.clone(), value.clone(), *pos));
840            }
841        }
842    }
843    all_fields.sort_by_key(|(_, _, pos)| *pos);
844
845    // Determine the sequence start marker based on message type
846    let (primary_marker, secondary_marker) = if message_type.contains("MT920Sequence") {
847        ("12", None)
848    } else if message_type.contains("MT935RateChange") {
849        ("23", Some("25"))
850    } else if message_type.contains("MT940StatementLine")
851        || message_type.contains("MT942StatementLine")
852    {
853        ("61", None)
854    } else {
855        ("21", None)
856    };
857
858    let mut sequences = Vec::new();
859    let mut current_sequence_fields: HashMap<String, Vec<(String, usize)>> = HashMap::new();
860    let mut in_sequence = false;
861
862    for (tag, value, pos) in all_fields {
863        // Check if this is the start of a new sequence
864        let is_sequence_start = (tag == primary_marker
865            || secondary_marker.is_some_and(|m| tag == m))
866            && !tag.ends_with("R")
867            && !tag.ends_with("F")
868            && !tag.ends_with("C")
869            && !tag.ends_with("D");
870
871        if is_sequence_start {
872            // If we were already in a sequence, parse the previous one
873            if in_sequence && !current_sequence_fields.is_empty() {
874                if let Ok(sequence_item) = T::from_fields(current_sequence_fields.clone()) {
875                    sequences.push(sequence_item);
876                }
877                current_sequence_fields.clear();
878            }
879            in_sequence = true;
880        }
881
882        // Add field to current sequence if we're in one
883        if in_sequence {
884            current_sequence_fields
885                .entry(tag.clone())
886                .or_default()
887                .push((value, pos));
888
889            // Mark this field as consumed
890            tracker.mark_consumed(&tag, pos);
891        }
892    }
893
894    // Parse the last sequence if there is one
895    if in_sequence && !current_sequence_fields.is_empty() {
896        match T::from_fields(current_sequence_fields) {
897            Ok(sequence_item) => {
898                sequences.push(sequence_item);
899            }
900            Err(_e) => {
901                #[cfg(debug_assertions)]
902                eprintln!("DEBUG: Failed to parse final sequence item: {_e:?}");
903            }
904        }
905    }
906
907    Ok(sequences)
908}
909
910/// Parse sequence B items from already split fields
911fn parse_sequence_b_items<T>(
912    fields: &HashMap<String, Vec<(String, usize)>>,
913    tracker: &mut FieldConsumptionTracker,
914) -> Result<Vec<T>>
915where
916    T: crate::SwiftMessageBody,
917{
918    let mut sequences = Vec::new();
919
920    // Get all fields sorted by position
921    let mut all_fields: Vec<(String, String, usize)> = Vec::new();
922    for (tag, values) in fields {
923        for (value, pos) in values {
924            all_fields.push((tag.clone(), value.clone(), *pos));
925        }
926    }
927    all_fields.sort_by_key(|(_, _, pos)| *pos);
928
929    // Determine the sequence start tag based on message type
930    let message_type = std::any::type_name::<T>();
931    let sequence_start_tag = if message_type.contains("MT204Transaction") {
932        "20" // MT204 transactions start with field 20
933    } else {
934        "21" // Most other transactions start with field 21
935    };
936    let mut current_sequence_fields: HashMap<String, Vec<(String, usize)>> = HashMap::new();
937    let mut in_sequence = false;
938
939    for (tag, value, pos) in all_fields {
940        // Check if this is the start of a new sequence
941        if tag == sequence_start_tag
942            && !tag.ends_with("R")
943            && !tag.ends_with("F")
944            && !tag.ends_with("C")
945            && !tag.ends_with("D")
946        {
947            // If we were already in a sequence, parse the previous one
948            if in_sequence && !current_sequence_fields.is_empty() {
949                if let Ok(sequence_item) = T::from_fields(current_sequence_fields.clone()) {
950                    sequences.push(sequence_item);
951                }
952                current_sequence_fields.clear();
953            }
954            in_sequence = true;
955        }
956
957        // Add field to current sequence if we're in one
958        if in_sequence {
959            current_sequence_fields
960                .entry(tag.clone())
961                .or_default()
962                .push((value, pos));
963
964            // Mark this field as consumed
965            tracker.mark_consumed(&tag, pos);
966        }
967    }
968
969    // Parse the last sequence if there is one
970    if in_sequence && !current_sequence_fields.is_empty() {
971        match T::from_fields(current_sequence_fields) {
972            Ok(sequence_item) => {
973                sequences.push(sequence_item);
974            }
975            Err(_e) => {
976                #[cfg(debug_assertions)]
977                eprintln!("DEBUG: Failed to parse final sequence item: {_e:?}");
978            }
979        }
980    }
981
982    Ok(sequences)
983}
984
985/// Serialize a SwiftMessage field map to a string representation
986/// This is a placeholder implementation for the macro system
987pub fn serialize_swift_message_to_string(fields: &HashMap<String, Vec<String>>) -> String {
988    // For now, this is a stub implementation
989    // In a real implementation, this would serialize the field map
990    // into a string representation of a SwiftMessage
991
992    let mut result = String::new();
993
994    // Simple serialization: just join all field values with newlines
995    for field_values in fields.values() {
996        for field_value in field_values {
997            // field_value should already be in the format ":XX:value"
998            result.push_str(field_value);
999            result.push('\n');
1000        }
1001    }
1002
1003    // Remove trailing newline
1004    if result.ends_with('\n') {
1005        result.pop();
1006    }
1007
1008    result
1009}