swift_mt_message/
errors.rs

1//! # Error Handling
2//!
3//! Comprehensive error types for SWIFT MT message parsing and validation.
4//!
5//! ## Error Types
6//! - **ParseError**: Message parsing and field extraction failures
7//! - **ValidationError**: Field format and business rule violations
8//! - **SwiftValidationError**: SWIFT network validation (1,335 error codes: T/C/D/E/G series)
9//!
10//! ## Example
11//! ```rust
12//! use swift_mt_message::parser::SwiftParser;
13//! use swift_mt_message::ParseError;
14//!
15//! # let msg = "{1:F01BANKDEFF...}{2:I103...}{4:-}";
16//! match SwiftParser::parse_auto(msg) {
17//!     Ok(message) => println!("Success"),
18//!     Err(ParseError::InvalidFieldFormat(err)) => {
19//!         eprintln!("Field {}: {}", err.field_tag, err.component_name);
20//!     },
21//!     Err(other) => eprintln!("{}", other),
22//! }
23//! ```
24
25use serde::{Deserialize, Serialize};
26use std::any::Any;
27use std::fmt::{self, Display, Formatter};
28use thiserror::Error;
29
30/// Result type alias for the library
31///
32/// Standard Result type used throughout the library for consistent error handling.
33/// All fallible operations return `Result<T>` where T is the success type.
34pub type Result<T> = std::result::Result<T, ParseError>;
35
36/// Enhanced result type for SWIFT validation operations
37pub type SwiftValidationResult<T> = std::result::Result<T, SwiftValidationError>;
38
39/// Collection of parsing errors for comprehensive error reporting
40#[derive(Debug, Serialize, Deserialize)]
41pub struct ParseErrorCollection {
42    /// All parsing errors encountered
43    pub errors: Vec<ParseError>,
44    /// Partial parse result if available (not cloneable due to dyn Any)
45    #[serde(skip)]
46    pub partial_result: Option<Box<dyn Any>>,
47}
48
49/// Result of parsing a field with potential error
50#[derive(Debug, Clone)]
51pub struct FieldParseResult<T> {
52    /// Successfully parsed value (if any)
53    pub value: Option<T>,
54    /// Error encountered during parsing (if any)
55    pub error: Option<ParseError>,
56}
57
58/// Result type for parse operations with error collection
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub enum ParseResult<T> {
61    /// Parsing succeeded without errors
62    Success(T),
63    /// Parsing succeeded with non-critical errors
64    PartialSuccess(T, Vec<ParseError>),
65    /// Parsing failed with critical errors
66    Failure(Vec<ParseError>),
67}
68
69/// Parser configuration options
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct ParserConfig {
72    /// If true, stop at first error (default: false)
73    pub fail_fast: bool,
74    /// If true, validate optional fields (default: true)
75    pub validate_optional_fields: bool,
76    /// If true, collect all errors even for non-critical issues (default: true)
77    pub collect_all_errors: bool,
78}
79
80impl Default for ParserConfig {
81    fn default() -> Self {
82        Self {
83            fail_fast: false,
84            validate_optional_fields: true,
85            collect_all_errors: true,
86        }
87    }
88}
89
90impl Display for ParseErrorCollection {
91    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
92        writeln!(f, "Found {} parsing errors:", self.errors.len())?;
93        for (idx, error) in self.errors.iter().enumerate() {
94            writeln!(f, "\n{}. {}", idx + 1, error)?;
95        }
96        Ok(())
97    }
98}
99
100impl std::error::Error for ParseErrorCollection {}
101
102/// Details for invalid field format errors
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct InvalidFieldFormatError {
105    /// SWIFT field tag (e.g., "50K", "32A")
106    pub field_tag: String,
107    /// Component name within the field (e.g., "currency", "amount")
108    pub component_name: String,
109    /// The actual value that failed to parse
110    pub value: String,
111    /// Expected format specification
112    pub format_spec: String,
113    /// Position in the original message
114    pub position: Option<usize>,
115    /// Inner parsing error (simplified for serialization)
116    pub inner_error: String,
117}
118
119/// Main error type for parsing operations
120#[derive(Error, Debug, Clone, Serialize, Deserialize)]
121pub enum ParseError {
122    #[error("Wrong message type: expected {expected}, got {actual}")]
123    WrongMessageType { expected: String, actual: String },
124
125    #[error("Unsupported message type: {message_type}")]
126    UnsupportedMessageType { message_type: String },
127
128    #[error("Field validation failed: {errors:?}")]
129    ValidationFailed { errors: Vec<ValidationError> },
130
131    #[error("IO error: {message}")]
132    IoError { message: String },
133
134    #[error(transparent)]
135    SwiftValidation(Box<SwiftValidationError>),
136
137    #[error("Serialization error: {message}")]
138    SerializationError { message: String },
139
140    /// Invalid message format error
141    #[error("Invalid message format: {message}")]
142    InvalidFormat { message: String },
143
144    /// Field format error with full context
145    #[error("Invalid field format - Field: {}, Component: {}, Value: '{}', Expected: {}", .0.field_tag, .0.component_name, .0.value, .0.format_spec)]
146    InvalidFieldFormat(Box<InvalidFieldFormatError>),
147
148    /// Missing required field with detailed context
149    #[error("Missing required field {field_tag} ({field_name}) in {message_type}")]
150    MissingRequiredField {
151        /// SWIFT field tag
152        field_tag: String,
153        /// Rust field name in the struct
154        field_name: String,
155        /// Message type (MT103, MT202, etc.)
156        message_type: String,
157        /// Position where field was expected
158        position_in_block4: Option<usize>,
159    },
160
161    /// Field parsing failed with detailed context
162    #[error(
163        "Failed to parse field {field_tag} of type {field_type} at line {position}: {original_error}"
164    )]
165    FieldParsingFailed {
166        /// SWIFT field tag
167        field_tag: String,
168        /// Type of field being parsed
169        field_type: String,
170        /// Line number in message
171        position: usize,
172        /// Original error message
173        original_error: String,
174    },
175
176    /// Component parsing error with specific details
177    #[error(
178        "Component parse error in field {field_tag}: {component_name} (index {component_index})"
179    )]
180    ComponentParseError {
181        /// Field tag containing the component
182        field_tag: String,
183        /// Index of component in field
184        component_index: usize,
185        /// Name of the component
186        component_name: String,
187        /// Expected format
188        expected_format: String,
189        /// Actual value that failed
190        actual_value: String,
191    },
192
193    /// Invalid block structure with detailed location
194    #[error("Invalid block {block} structure: {message}")]
195    InvalidBlockStructure {
196        /// Block number (1-5)
197        block: String,
198        /// Detailed error message
199        message: String,
200    },
201
202    /// Multiple parsing errors collected during message parsing
203    #[error("Multiple parsing errors found ({} errors)", .0.len())]
204    MultipleErrors(Vec<ParseError>),
205}
206
207/// Validation error for field-level validation
208#[derive(Error, Debug, Clone, Serialize, Deserialize)]
209pub enum ValidationError {
210    #[error("Field {field_tag} format validation failed: {message}")]
211    FormatValidation { field_tag: String, message: String },
212
213    #[error("Field {field_tag} length validation failed: expected {expected}, got {actual}")]
214    LengthValidation {
215        field_tag: String,
216        expected: String,
217        actual: usize,
218    },
219
220    #[error("Field {field_tag} pattern validation failed: {message}")]
221    PatternValidation { field_tag: String, message: String },
222
223    #[error("Field {field_tag} value validation failed: {message}")]
224    ValueValidation { field_tag: String, message: String },
225
226    #[error("Business rule validation failed: {rule_name} - {message}")]
227    BusinessRuleValidation { rule_name: String, message: String },
228}
229
230/// Comprehensive SWIFT validation error system based on SWIFT Standard Error Codes
231///
232/// This error system implements all 1,335 SWIFT error codes across T, C, D, E, and G series
233/// to provide precise feedback matching SWIFT network validation standards.
234#[derive(Error, Debug, Clone, Serialize, Deserialize)]
235pub enum SwiftValidationError {
236    /// T-Series: Technical/Format Validation Errors (275 codes)
237    /// Format validation errors for field structure and basic syntax compliance
238    #[error(transparent)]
239    Format(Box<SwiftFormatError>),
240
241    /// C-Series: Conditional/Business Rules Errors (57 codes)
242    /// Business logic validation for conditional fields and cross-field relationships
243    #[error(transparent)]
244    Business(Box<SwiftBusinessError>),
245
246    /// D-Series: Data/Content Validation Errors (77 codes)
247    /// Content-specific validation including regional requirements and dependencies
248    #[error(transparent)]
249    Content(Box<SwiftContentError>),
250
251    /// E-Series: Enhanced/Field Relation Validation Errors (86 codes)
252    /// Advanced validation for instruction codes and complex business rules
253    #[error(transparent)]
254    Relation(Box<SwiftRelationError>),
255
256    /// G-Series: General/Field Validation Errors (823 codes)
257    /// General field validation across all MT categories
258    #[error(transparent)]
259    General(Box<SwiftGeneralError>),
260}
261
262/// T-Series: Technical/Format Validation Error
263#[derive(Error, Debug, Clone, Serialize, Deserialize)]
264#[error("Format Error {code}: Field {field} contains '{value}', expected {expected}. {message}")]
265pub struct SwiftFormatError {
266    /// SWIFT error code (e.g., "T50", "T27")
267    pub code: String,
268    /// Field tag where error occurred
269    pub field: String,
270    /// Invalid value that caused the error
271    pub value: String,
272    /// Expected format or value
273    pub expected: String,
274    /// Human-readable error message
275    pub message: String,
276    /// Additional context for error recovery
277    pub context: Option<String>,
278}
279
280/// C-Series: Conditional/Business Rules Error
281#[derive(Error, Debug, Clone, Serialize, Deserialize)]
282#[error("Business Rule Violation {code}: {message} (Field: {field})")]
283pub struct SwiftBusinessError {
284    /// SWIFT error code (e.g., "C02", "C81")
285    pub code: String,
286    /// Primary field tag involved
287    pub field: String,
288    /// Related field tags for cross-field validation
289    pub related_fields: Vec<String>,
290    /// Human-readable error message
291    pub message: String,
292    /// Business rule that was violated
293    pub rule_description: String,
294    /// Additional context for error recovery
295    pub context: Option<String>,
296}
297
298/// D-Series: Data/Content Validation Error
299#[derive(Error, Debug, Clone, Serialize, Deserialize)]
300#[error("Content Validation Error {code}: {message} (Field: {field})")]
301pub struct SwiftContentError {
302    /// SWIFT error code (e.g., "D19", "D49")
303    pub code: String,
304    /// Field tag where error occurred
305    pub field: String,
306    /// Invalid content that caused the error
307    pub content: String,
308    /// Human-readable error message
309    pub message: String,
310    /// Regional or contextual requirements
311    pub requirements: String,
312    /// Additional context for error recovery
313    pub context: Option<String>,
314}
315
316/// E-Series: Enhanced/Field Relation Validation Error
317#[derive(Error, Debug, Clone, Serialize, Deserialize)]
318#[error("Relation Validation Error {code}: {message} (Field: {field})")]
319pub struct SwiftRelationError {
320    /// SWIFT error code (e.g., "E01", "E15")
321    pub code: String,
322    /// Primary field tag involved
323    pub field: String,
324    /// Related field tags that affect this validation
325    pub related_fields: Vec<String>,
326    /// Instruction code or option that caused the error
327    pub instruction_context: Option<String>,
328    /// Human-readable error message
329    pub message: String,
330    /// Relationship rule that was violated
331    pub rule_description: String,
332    /// Additional context for error recovery
333    pub context: Option<String>,
334}
335
336/// G-Series: General/Field Validation Error
337#[derive(Error, Debug, Clone, Serialize, Deserialize)]
338#[error("General Validation Error {code}: {message} (Field: {field})")]
339pub struct SwiftGeneralError {
340    /// SWIFT error code (e.g., "G001", "G050")
341    pub code: String,
342    /// Field tag where error occurred
343    pub field: String,
344    /// Invalid value that caused the error
345    pub value: String,
346    /// Human-readable error message
347    pub message: String,
348    /// MT category context (1-9 or Common)
349    pub category: Option<String>,
350    /// Additional context for error recovery
351    pub context: Option<String>,
352}
353
354impl From<std::io::Error> for ParseError {
355    fn from(err: std::io::Error) -> Self {
356        ParseError::IoError {
357            message: err.to_string(),
358        }
359    }
360}
361
362impl ParseError {
363    /// Get a detailed debug report for the error
364    pub fn debug_report(&self) -> String {
365        match self {
366            ParseError::InvalidFieldFormat(err) => {
367                format!(
368                    "Field Parsing Error:\n\
369                     ├─ Field Tag: {}\n\
370                     ├─ Component: {}\n\
371                     ├─ Value: '{}'\n\
372                     ├─ Expected Format: {}\n\
373                     ├─ Position in Message: {}\n\
374                     ├─ Details: {}\n\
375                     └─ Hint: Check SWIFT format specification for field {}",
376                    err.field_tag,
377                    err.component_name,
378                    err.value,
379                    err.format_spec,
380                    err.position
381                        .map_or("unknown".to_string(), |p| p.to_string()),
382                    err.inner_error,
383                    err.field_tag
384                )
385            }
386            ParseError::MissingRequiredField {
387                field_tag,
388                field_name,
389                message_type,
390                position_in_block4,
391            } => {
392                format!(
393                    "Missing Required Field:\n\
394                     ├─ Field Tag: {}\n\
395                     ├─ Field Name: {}\n\
396                     ├─ Message Type: {}\n\
397                     ├─ Expected Position: {}\n\
398                     └─ Hint: {} requires field {} to be present",
399                    field_tag,
400                    field_name,
401                    message_type,
402                    position_in_block4.map_or("unknown".to_string(), |p| p.to_string()),
403                    message_type,
404                    field_tag
405                )
406            }
407            ParseError::ComponentParseError {
408                field_tag,
409                component_index,
410                component_name,
411                expected_format,
412                actual_value,
413            } => {
414                format!(
415                    "Component Parse Error:\n\
416                     ├─ Field Tag: {field_tag}\n\
417                     ├─ Component: {component_name} (index {component_index})\n\
418                     ├─ Expected Format: {expected_format}\n\
419                     ├─ Actual Value: '{actual_value}'\n\
420                     └─ Hint: Component '{component_name}' must match format '{expected_format}'"
421                )
422            }
423            ParseError::FieldParsingFailed {
424                field_tag,
425                field_type,
426                position,
427                original_error,
428            } => {
429                let line_num = if *position > 0xFFFF {
430                    // Old format: encoded position
431                    position >> 16
432                } else {
433                    // New format: just line number
434                    *position
435                };
436                format!(
437                    "Field Parsing Failed:\n\
438                     ├─ Field Tag: {field_tag}\n\
439                     ├─ Field Type: {field_type}\n\
440                     ├─ Line Number: {line_num}\n\
441                     ├─ Error: {original_error}\n\
442                     └─ Hint: Check the field value matches the expected type"
443                )
444            }
445            ParseError::InvalidBlockStructure { block, message } => {
446                format!(
447                    "Block Structure Error:\n\
448                     ├─ Block: {block}\n\
449                     ├─ Error: {message}\n\
450                     └─ Hint: Ensure block {block} follows SWIFT message structure"
451                )
452            }
453            ParseError::MultipleErrors(errors) => {
454                let mut output = format!("Multiple Parsing Errors ({} total):\n", errors.len());
455                for (idx, error) in errors.iter().enumerate() {
456                    output.push_str(&format!("\n{}. {}\n", idx + 1, error.debug_report()));
457                }
458                output
459            }
460            // Fallback for other variants
461            _ => format!("{self}"),
462        }
463    }
464
465    /// Get a concise error message for logging
466    pub fn brief_message(&self) -> String {
467        match self {
468            ParseError::InvalidFieldFormat(err) => {
469                format!(
470                    "Field {} component '{}' format error",
471                    err.field_tag, err.component_name
472                )
473            }
474            ParseError::MissingRequiredField {
475                field_tag,
476                message_type,
477                ..
478            } => {
479                format!("Required field {field_tag} missing in {message_type}")
480            }
481            ParseError::ComponentParseError {
482                field_tag,
483                component_name,
484                ..
485            } => {
486                format!("Field {field_tag} component '{component_name}' parse error")
487            }
488            ParseError::FieldParsingFailed {
489                field_tag,
490                field_type,
491                position,
492                ..
493            } => {
494                let line_num = if *position > 0xFFFF {
495                    position >> 16
496                } else {
497                    *position
498                };
499                format!("Field {field_tag} (type {field_type}) parsing failed at line {line_num}")
500            }
501            ParseError::InvalidBlockStructure { block, .. } => {
502                format!("Block {block} structure invalid")
503            }
504            ParseError::MultipleErrors(errors) => {
505                format!("{} parsing errors found", errors.len())
506            }
507            _ => self.to_string(),
508        }
509    }
510
511    /// Format error with message context
512    pub fn format_with_context(&self, original_message: &str) -> String {
513        match self {
514            ParseError::FieldParsingFailed { position, .. } => {
515                // Extract line and show context
516                let lines: Vec<&str> = original_message.lines().collect();
517                let line_num = if *position > 0xFFFF {
518                    position >> 16
519                } else {
520                    *position
521                };
522                let mut output = self.debug_report();
523
524                if line_num > 0 && line_num <= lines.len() {
525                    output.push_str("\n\nContext:\n");
526                    // Show 2 lines before and after
527                    let start = line_num.saturating_sub(3);
528                    let end = (line_num + 2).min(lines.len());
529
530                    for (i, line) in lines.iter().enumerate().take(end).skip(start) {
531                        if i == line_num - 1 {
532                            output.push_str(&format!(">>> {} │ {}\n", i + 1, line));
533                        } else {
534                            output.push_str(&format!("    {} │ {}\n", i + 1, line));
535                        }
536                    }
537                }
538                output
539            }
540            ParseError::InvalidFieldFormat(err) if err.position.is_some() => {
541                let lines: Vec<&str> = original_message.lines().collect();
542                let pos = err.position.unwrap();
543                let line_num = pos >> 16;
544                let mut output = self.debug_report();
545
546                if line_num > 0 && line_num <= lines.len() {
547                    output.push_str("\n\nContext:\n");
548                    let start = line_num.saturating_sub(3);
549                    let end = (line_num + 2).min(lines.len());
550
551                    for (i, line) in lines.iter().enumerate().take(end).skip(start) {
552                        if i == line_num - 1 {
553                            output.push_str(&format!(">>> {} │ {}\n", i + 1, line));
554                        } else {
555                            output.push_str(&format!("    {} │ {}\n", i + 1, line));
556                        }
557                    }
558                }
559                output
560            }
561            _ => self.debug_report(),
562        }
563    }
564}
565
566impl From<serde_json::Error> for ParseError {
567    fn from(err: serde_json::Error) -> Self {
568        ParseError::SerializationError {
569            message: err.to_string(),
570        }
571    }
572}
573
574/// SWIFT Error Code Constants
575///
576/// This module contains all official SWIFT error codes organized by series.
577/// Total: 1,335 unique error codes across all MT categories.
578pub mod error_codes {
579    /// T-Series: Technical/Format Validation Error Codes (275 codes)
580    pub mod format {
581        pub const T08: &str = "T08"; // Invalid code in field
582        pub const T26: &str = "T26"; // Invalid slash usage
583        pub const T27: &str = "T27"; // Invalid BIC code format
584        pub const T28: &str = "T28"; // Invalid BIC code length
585        pub const T29: &str = "T29"; // Invalid BIC code structure
586        pub const T40: &str = "T40"; // Invalid amount format
587        pub const T43: &str = "T43"; // Amount exceeds maximum digits
588        pub const T45: &str = "T45"; // Invalid identifier code format
589        pub const T50: &str = "T50"; // Invalid date format
590        pub const T52: &str = "T52"; // Invalid currency code
591        pub const T56: &str = "T56"; // Invalid structured address
592        pub const T73: &str = "T73"; // Invalid country code
593        // Additional T-series codes will be added as needed
594    }
595
596    /// C-Series: Conditional/Business Rules Error Codes (57 codes)
597    pub mod business {
598        pub const C02: &str = "C02"; // Currency code mismatch
599        pub const C03: &str = "C03"; // Amount format validation
600        pub const C08: &str = "C08"; // Commodity currency not allowed
601        pub const C81: &str = "C81"; // Conditional field dependency
602        // Additional C-series codes will be added as needed
603    }
604
605    /// D-Series: Data/Content Validation Error Codes (77 codes)
606    pub mod content {
607        pub const D17: &str = "D17"; // Field presence requirement
608        pub const D18: &str = "D18"; // Mutually exclusive placement
609        pub const D19: &str = "D19"; // IBAN mandatory for SEPA
610        pub const D20: &str = "D20"; // Field 71A presence rules
611        pub const D22: &str = "D22"; // Exchange rate dependency
612        pub const D49: &str = "D49"; // Field 33B mandatory for EU
613        pub const D50: &str = "D50"; // SHA charge restrictions
614        pub const D51: &str = "D51"; // Field 33B with charge fields
615        pub const D75: &str = "D75"; // Exchange rate mandatory
616        pub const D79: &str = "D79"; // Field 71G dependency
617        pub const D93: &str = "D93"; // Account restrictions by code
618        // Additional D-series codes will be added as needed
619    }
620
621    /// E-Series: Enhanced/Field Relation Validation Error Codes (86 codes)
622    pub mod relation {
623        pub const E01: &str = "E01"; // Instruction code restrictions
624        pub const E02: &str = "E02"; // Prohibited instruction codes
625        pub const E03: &str = "E03"; // Field option restrictions
626        pub const E04: &str = "E04"; // Party identifier requirements
627        pub const E05: &str = "E05"; // Field 54A option restrictions
628        pub const E06: &str = "E06"; // Multiple field dependency
629        pub const E07: &str = "E07"; // Field 55A option restrictions
630        pub const E09: &str = "E09"; // Party identifier mandatory
631        pub const E10: &str = "E10"; // Beneficiary account mandatory
632        pub const E13: &str = "E13"; // OUR charge restrictions
633        pub const E15: &str = "E15"; // BEN charge requirements
634        pub const E16: &str = "E16"; // Field restrictions with SPRI
635        pub const E17: &str = "E17"; // Clearing code requirements
636        pub const E18: &str = "E18"; // Account restrictions CHQB
637        pub const E44: &str = "E44"; // Instruction code dependencies
638        pub const E45: &str = "E45"; // Instruction code field dependencies
639        // Additional E-series codes will be added as needed
640    }
641
642    /// G-Series: General/Field Validation Error Codes (823 codes)
643    pub mod general {
644        pub const G001: &str = "G001"; // Field format violation
645        pub const G050: &str = "G050"; // Field content validation
646        pub const G100: &str = "G100"; // Sequence validation
647        // Additional G-series codes will be added as needed
648    }
649}
650
651/// Helper functions for creating SWIFT validation errors
652impl SwiftValidationError {
653    /// Get the SWIFT error code for this validation error
654    pub fn error_code(&self) -> &str {
655        match self {
656            SwiftValidationError::Format(err) => &err.code,
657            SwiftValidationError::Business(err) => &err.code,
658            SwiftValidationError::Content(err) => &err.code,
659            SwiftValidationError::Relation(err) => &err.code,
660            SwiftValidationError::General(err) => &err.code,
661        }
662    }
663
664    /// Create a T-series format validation error
665    pub fn format_error(
666        code: &str,
667        field: &str,
668        value: &str,
669        expected: &str,
670        message: &str,
671    ) -> Self {
672        SwiftValidationError::Format(Box::new(SwiftFormatError {
673            code: code.to_string(),
674            field: field.to_string(),
675            value: value.to_string(),
676            expected: expected.to_string(),
677            message: message.to_string(),
678            context: None,
679        }))
680    }
681
682    /// Create a C-series business rule validation error
683    pub fn business_error(
684        code: &str,
685        field: &str,
686        related_fields: Vec<String>,
687        message: &str,
688        rule_description: &str,
689    ) -> Self {
690        SwiftValidationError::Business(Box::new(SwiftBusinessError {
691            code: code.to_string(),
692            field: field.to_string(),
693            related_fields,
694            message: message.to_string(),
695            rule_description: rule_description.to_string(),
696            context: None,
697        }))
698    }
699
700    /// Create a D-series content validation error
701    pub fn content_error(
702        code: &str,
703        field: &str,
704        content: &str,
705        message: &str,
706        requirements: &str,
707    ) -> Self {
708        SwiftValidationError::Content(Box::new(SwiftContentError {
709            code: code.to_string(),
710            field: field.to_string(),
711            content: content.to_string(),
712            message: message.to_string(),
713            requirements: requirements.to_string(),
714            context: None,
715        }))
716    }
717
718    /// Create an E-series relation validation error
719    pub fn relation_error(
720        code: &str,
721        field: &str,
722        related_fields: Vec<String>,
723        message: &str,
724        rule_description: &str,
725    ) -> Self {
726        SwiftValidationError::Relation(Box::new(SwiftRelationError {
727            code: code.to_string(),
728            field: field.to_string(),
729            related_fields,
730            instruction_context: None,
731            message: message.to_string(),
732            rule_description: rule_description.to_string(),
733            context: None,
734        }))
735    }
736
737    /// Create a G-series general validation error
738    pub fn general_error(
739        code: &str,
740        field: &str,
741        value: &str,
742        message: &str,
743        category: Option<&str>,
744    ) -> Self {
745        SwiftValidationError::General(Box::new(SwiftGeneralError {
746            code: code.to_string(),
747            field: field.to_string(),
748            value: value.to_string(),
749            message: message.to_string(),
750            category: category.map(|s| s.to_string()),
751            context: None,
752        }))
753    }
754
755    /// Get the error code from any SWIFT validation error
756    pub fn code(&self) -> &str {
757        match self {
758            SwiftValidationError::Format(err) => &err.code,
759            SwiftValidationError::Business(err) => &err.code,
760            SwiftValidationError::Content(err) => &err.code,
761            SwiftValidationError::Relation(err) => &err.code,
762            SwiftValidationError::General(err) => &err.code,
763        }
764    }
765
766    /// Get the field tag from any SWIFT validation error
767    pub fn field(&self) -> &str {
768        match self {
769            SwiftValidationError::Format(err) => &err.field,
770            SwiftValidationError::Business(err) => &err.field,
771            SwiftValidationError::Content(err) => &err.field,
772            SwiftValidationError::Relation(err) => &err.field,
773            SwiftValidationError::General(err) => &err.field,
774        }
775    }
776
777    /// Get the error message from any SWIFT validation error
778    pub fn message(&self) -> &str {
779        match self {
780            SwiftValidationError::Format(err) => &err.message,
781            SwiftValidationError::Business(err) => &err.message,
782            SwiftValidationError::Content(err) => &err.message,
783            SwiftValidationError::Relation(err) => &err.message,
784            SwiftValidationError::General(err) => &err.message,
785        }
786    }
787}
788
789/// Convert SwiftValidationError to ValidationError for backward compatibility
790impl From<SwiftValidationError> for ValidationError {
791    fn from(swift_error: SwiftValidationError) -> Self {
792        match swift_error {
793            SwiftValidationError::Format(err) => ValidationError::FormatValidation {
794                field_tag: err.field,
795                message: format!("{}: {}", err.code, err.message),
796            },
797            SwiftValidationError::Business(err) => ValidationError::BusinessRuleValidation {
798                rule_name: err.code,
799                message: err.message,
800            },
801            SwiftValidationError::Content(err) => ValidationError::ValueValidation {
802                field_tag: err.field,
803                message: format!("{}: {}", err.code, err.message),
804            },
805            SwiftValidationError::Relation(err) => ValidationError::BusinessRuleValidation {
806                rule_name: err.code,
807                message: err.message,
808            },
809            SwiftValidationError::General(err) => ValidationError::FormatValidation {
810                field_tag: err.field,
811                message: format!("{}: {}", err.code, err.message),
812            },
813        }
814    }
815}
816
817/// Convert SwiftValidationError to ParseError
818impl From<SwiftValidationError> for ParseError {
819    fn from(validation_error: SwiftValidationError) -> Self {
820        ParseError::SwiftValidation(Box::new(validation_error))
821    }
822}
823
824/// Convert ValidationError to SwiftValidationError
825impl From<ValidationError> for SwiftValidationError {
826    fn from(validation_error: ValidationError) -> Self {
827        match validation_error {
828            ValidationError::FormatValidation { field_tag, message } => {
829                SwiftValidationError::format_error("T00", &field_tag, "", "", &message)
830            }
831            ValidationError::LengthValidation {
832                field_tag,
833                expected,
834                actual,
835            } => SwiftValidationError::format_error(
836                "T00",
837                &field_tag,
838                &actual.to_string(),
839                &expected,
840                "Length validation failed",
841            ),
842            ValidationError::PatternValidation { field_tag, message } => {
843                SwiftValidationError::format_error("T00", &field_tag, "", "", &message)
844            }
845            ValidationError::ValueValidation { field_tag, message } => {
846                SwiftValidationError::content_error("D00", &field_tag, "", &message, "")
847            }
848            ValidationError::BusinessRuleValidation { rule_name, message } => {
849                SwiftValidationError::business_error(&rule_name, "", vec![], &message, "")
850            }
851        }
852    }
853}