swift_mt_message/
errors.rs

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