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(
206        "Failed to parse field {field_tag} of type {field_type} at line {position}: {original_error}"
207    )]
208    FieldParsingFailed {
209        /// SWIFT field tag
210        field_tag: String,
211        /// Type of field being parsed
212        field_type: String,
213        /// Line number in message
214        position: usize,
215        /// Original error message
216        original_error: String,
217    },
218
219    /// Component parsing error with specific details
220    #[error(
221        "Component parse error in field {field_tag}: {component_name} (index {component_index})"
222    )]
223    ComponentParseError {
224        /// Field tag containing the component
225        field_tag: String,
226        /// Index of component in field
227        component_index: usize,
228        /// Name of the component
229        component_name: String,
230        /// Expected format
231        expected_format: String,
232        /// Actual value that failed
233        actual_value: String,
234    },
235
236    /// Invalid block structure with detailed location
237    #[error("Invalid block {block} structure: {message}")]
238    InvalidBlockStructure {
239        /// Block number (1-5)
240        block: String,
241        /// Detailed error message
242        message: String,
243    },
244
245    /// Multiple parsing errors collected during message parsing
246    #[error("Multiple parsing errors found ({} errors)", .0.len())]
247    MultipleErrors(Vec<ParseError>),
248}
249
250/// Validation error for field-level validation
251#[derive(Error, Debug, Clone, Serialize, Deserialize)]
252pub enum ValidationError {
253    #[error("Field {field_tag} format validation failed: {message}")]
254    FormatValidation { field_tag: String, message: String },
255
256    #[error("Field {field_tag} length validation failed: expected {expected}, got {actual}")]
257    LengthValidation {
258        field_tag: String,
259        expected: String,
260        actual: usize,
261    },
262
263    #[error("Field {field_tag} pattern validation failed: {message}")]
264    PatternValidation { field_tag: String, message: String },
265
266    #[error("Field {field_tag} value validation failed: {message}")]
267    ValueValidation { field_tag: String, message: String },
268
269    #[error("Business rule validation failed: {rule_name} - {message}")]
270    BusinessRuleValidation { rule_name: String, message: String },
271}
272
273/// Comprehensive SWIFT validation error system based on SWIFT Standard Error Codes
274///
275/// This error system implements all 1,335 SWIFT error codes across T, C, D, E, and G series
276/// to provide precise feedback matching SWIFT network validation standards.
277#[derive(Error, Debug, Clone, Serialize, Deserialize)]
278pub enum SwiftValidationError {
279    /// T-Series: Technical/Format Validation Errors (275 codes)
280    /// Format validation errors for field structure and basic syntax compliance
281    #[error(transparent)]
282    Format(Box<SwiftFormatError>),
283
284    /// C-Series: Conditional/Business Rules Errors (57 codes)
285    /// Business logic validation for conditional fields and cross-field relationships
286    #[error(transparent)]
287    Business(Box<SwiftBusinessError>),
288
289    /// D-Series: Data/Content Validation Errors (77 codes)
290    /// Content-specific validation including regional requirements and dependencies
291    #[error(transparent)]
292    Content(Box<SwiftContentError>),
293
294    /// E-Series: Enhanced/Field Relation Validation Errors (86 codes)
295    /// Advanced validation for instruction codes and complex business rules
296    #[error(transparent)]
297    Relation(Box<SwiftRelationError>),
298
299    /// G-Series: General/Field Validation Errors (823 codes)
300    /// General field validation across all MT categories
301    #[error(transparent)]
302    General(Box<SwiftGeneralError>),
303}
304
305/// T-Series: Technical/Format Validation Error
306#[derive(Error, Debug, Clone, Serialize, Deserialize)]
307#[error("Format Error {code}: Field {field} contains '{value}', expected {expected}. {message}")]
308pub struct SwiftFormatError {
309    /// SWIFT error code (e.g., "T50", "T27")
310    pub code: String,
311    /// Field tag where error occurred
312    pub field: String,
313    /// Invalid value that caused the error
314    pub value: String,
315    /// Expected format or value
316    pub expected: String,
317    /// Human-readable error message
318    pub message: String,
319    /// Additional context for error recovery
320    pub context: Option<String>,
321}
322
323/// C-Series: Conditional/Business Rules Error
324#[derive(Error, Debug, Clone, Serialize, Deserialize)]
325#[error("Business Rule Violation {code}: {message} (Field: {field})")]
326pub struct SwiftBusinessError {
327    /// SWIFT error code (e.g., "C02", "C81")
328    pub code: String,
329    /// Primary field tag involved
330    pub field: String,
331    /// Related field tags for cross-field validation
332    pub related_fields: Vec<String>,
333    /// Human-readable error message
334    pub message: String,
335    /// Business rule that was violated
336    pub rule_description: String,
337    /// Additional context for error recovery
338    pub context: Option<String>,
339}
340
341/// D-Series: Data/Content Validation Error
342#[derive(Error, Debug, Clone, Serialize, Deserialize)]
343#[error("Content Validation Error {code}: {message} (Field: {field})")]
344pub struct SwiftContentError {
345    /// SWIFT error code (e.g., "D19", "D49")
346    pub code: String,
347    /// Field tag where error occurred
348    pub field: String,
349    /// Invalid content that caused the error
350    pub content: String,
351    /// Human-readable error message
352    pub message: String,
353    /// Regional or contextual requirements
354    pub requirements: String,
355    /// Additional context for error recovery
356    pub context: Option<String>,
357}
358
359/// E-Series: Enhanced/Field Relation Validation Error
360#[derive(Error, Debug, Clone, Serialize, Deserialize)]
361#[error("Relation Validation Error {code}: {message} (Field: {field})")]
362pub struct SwiftRelationError {
363    /// SWIFT error code (e.g., "E01", "E15")
364    pub code: String,
365    /// Primary field tag involved
366    pub field: String,
367    /// Related field tags that affect this validation
368    pub related_fields: Vec<String>,
369    /// Instruction code or option that caused the error
370    pub instruction_context: Option<String>,
371    /// Human-readable error message
372    pub message: String,
373    /// Relationship rule that was violated
374    pub rule_description: String,
375    /// Additional context for error recovery
376    pub context: Option<String>,
377}
378
379/// G-Series: General/Field Validation Error
380#[derive(Error, Debug, Clone, Serialize, Deserialize)]
381#[error("General Validation Error {code}: {message} (Field: {field})")]
382pub struct SwiftGeneralError {
383    /// SWIFT error code (e.g., "G001", "G050")
384    pub code: String,
385    /// Field tag where error occurred
386    pub field: String,
387    /// Invalid value that caused the error
388    pub value: String,
389    /// Human-readable error message
390    pub message: String,
391    /// MT category context (1-9 or Common)
392    pub category: Option<String>,
393    /// Additional context for error recovery
394    pub context: Option<String>,
395}
396
397impl From<std::io::Error> for ParseError {
398    fn from(err: std::io::Error) -> Self {
399        ParseError::IoError {
400            message: err.to_string(),
401        }
402    }
403}
404
405impl ParseError {
406    /// Get a detailed debug report for the error
407    pub fn debug_report(&self) -> String {
408        match self {
409            ParseError::InvalidFieldFormat(err) => {
410                format!(
411                    "Field Parsing Error:\n\
412                     ├─ Field Tag: {}\n\
413                     ├─ Component: {}\n\
414                     ├─ Value: '{}'\n\
415                     ├─ Expected Format: {}\n\
416                     ├─ Position in Message: {}\n\
417                     ├─ Details: {}\n\
418                     └─ Hint: Check SWIFT format specification for field {}",
419                    err.field_tag,
420                    err.component_name,
421                    err.value,
422                    err.format_spec,
423                    err.position
424                        .map_or("unknown".to_string(), |p| p.to_string()),
425                    err.inner_error,
426                    err.field_tag
427                )
428            }
429            ParseError::MissingRequiredField {
430                field_tag,
431                field_name,
432                message_type,
433                position_in_block4,
434            } => {
435                format!(
436                    "Missing Required Field:\n\
437                     ├─ Field Tag: {}\n\
438                     ├─ Field Name: {}\n\
439                     ├─ Message Type: {}\n\
440                     ├─ Expected Position: {}\n\
441                     └─ Hint: {} requires field {} to be present",
442                    field_tag,
443                    field_name,
444                    message_type,
445                    position_in_block4.map_or("unknown".to_string(), |p| p.to_string()),
446                    message_type,
447                    field_tag
448                )
449            }
450            ParseError::ComponentParseError {
451                field_tag,
452                component_index,
453                component_name,
454                expected_format,
455                actual_value,
456            } => {
457                format!(
458                    "Component Parse Error:\n\
459                     ├─ Field Tag: {field_tag}\n\
460                     ├─ Component: {component_name} (index {component_index})\n\
461                     ├─ Expected Format: {expected_format}\n\
462                     ├─ Actual Value: '{actual_value}'\n\
463                     └─ Hint: Component '{component_name}' must match format '{expected_format}'"
464                )
465            }
466            ParseError::FieldParsingFailed {
467                field_tag,
468                field_type,
469                position,
470                original_error,
471            } => {
472                let line_num = if *position > 0xFFFF {
473                    // Old format: encoded position
474                    position >> 16
475                } else {
476                    // New format: just line number
477                    *position
478                };
479                format!(
480                    "Field Parsing Failed:\n\
481                     ├─ Field Tag: {field_tag}\n\
482                     ├─ Field Type: {field_type}\n\
483                     ├─ Line Number: {line_num}\n\
484                     ├─ Error: {original_error}\n\
485                     └─ Hint: Check the field value matches the expected type"
486                )
487            }
488            ParseError::InvalidBlockStructure { block, message } => {
489                format!(
490                    "Block Structure Error:\n\
491                     ├─ Block: {block}\n\
492                     ├─ Error: {message}\n\
493                     └─ Hint: Ensure block {block} follows SWIFT message structure"
494                )
495            }
496            ParseError::MultipleErrors(errors) => {
497                let mut output = format!("Multiple Parsing Errors ({} total):\n", errors.len());
498                for (idx, error) in errors.iter().enumerate() {
499                    output.push_str(&format!("\n{}. {}\n", idx + 1, error.debug_report()));
500                }
501                output
502            }
503            // Fallback for other variants
504            _ => format!("{self}"),
505        }
506    }
507
508    /// Get a concise error message for logging
509    pub fn brief_message(&self) -> String {
510        match self {
511            ParseError::InvalidFieldFormat(err) => {
512                format!(
513                    "Field {} component '{}' format error",
514                    err.field_tag, err.component_name
515                )
516            }
517            ParseError::MissingRequiredField {
518                field_tag,
519                message_type,
520                ..
521            } => {
522                format!("Required field {field_tag} missing in {message_type}")
523            }
524            ParseError::ComponentParseError {
525                field_tag,
526                component_name,
527                ..
528            } => {
529                format!("Field {field_tag} component '{component_name}' parse error")
530            }
531            ParseError::FieldParsingFailed {
532                field_tag,
533                field_type,
534                position,
535                ..
536            } => {
537                let line_num = if *position > 0xFFFF {
538                    position >> 16
539                } else {
540                    *position
541                };
542                format!("Field {field_tag} (type {field_type}) parsing failed at line {line_num}")
543            }
544            ParseError::InvalidBlockStructure { block, .. } => {
545                format!("Block {block} structure invalid")
546            }
547            ParseError::MultipleErrors(errors) => {
548                format!("{} parsing errors found", errors.len())
549            }
550            _ => self.to_string(),
551        }
552    }
553
554    /// Format error with message context
555    pub fn format_with_context(&self, original_message: &str) -> String {
556        match self {
557            ParseError::FieldParsingFailed { position, .. } => {
558                // Extract line and show context
559                let lines: Vec<&str> = original_message.lines().collect();
560                let line_num = if *position > 0xFFFF {
561                    position >> 16
562                } else {
563                    *position
564                };
565                let mut output = self.debug_report();
566
567                if line_num > 0 && line_num <= lines.len() {
568                    output.push_str("\n\nContext:\n");
569                    // Show 2 lines before and after
570                    let start = line_num.saturating_sub(3);
571                    let end = (line_num + 2).min(lines.len());
572
573                    for (i, line) in lines.iter().enumerate().take(end).skip(start) {
574                        if i == line_num - 1 {
575                            output.push_str(&format!(">>> {} │ {}\n", i + 1, line));
576                        } else {
577                            output.push_str(&format!("    {} │ {}\n", i + 1, line));
578                        }
579                    }
580                }
581                output
582            }
583            ParseError::InvalidFieldFormat(err) if err.position.is_some() => {
584                let lines: Vec<&str> = original_message.lines().collect();
585                let pos = err.position.unwrap();
586                let line_num = pos >> 16;
587                let mut output = self.debug_report();
588
589                if line_num > 0 && line_num <= lines.len() {
590                    output.push_str("\n\nContext:\n");
591                    let start = line_num.saturating_sub(3);
592                    let end = (line_num + 2).min(lines.len());
593
594                    for (i, line) in lines.iter().enumerate().take(end).skip(start) {
595                        if i == line_num - 1 {
596                            output.push_str(&format!(">>> {} │ {}\n", i + 1, line));
597                        } else {
598                            output.push_str(&format!("    {} │ {}\n", i + 1, line));
599                        }
600                    }
601                }
602                output
603            }
604            _ => self.debug_report(),
605        }
606    }
607}
608
609impl From<serde_json::Error> for ParseError {
610    fn from(err: serde_json::Error) -> Self {
611        ParseError::SerializationError {
612            message: err.to_string(),
613        }
614    }
615}
616
617/// SWIFT Error Code Constants
618///
619/// This module contains all official SWIFT error codes organized by series.
620/// Total: 1,335 unique error codes across all MT categories.
621pub mod error_codes {
622    /// T-Series: Technical/Format Validation Error Codes (275 codes)
623    pub mod format {
624        pub const T08: &str = "T08"; // Invalid code in field
625        pub const T26: &str = "T26"; // Invalid slash usage
626        pub const T27: &str = "T27"; // Invalid BIC code format
627        pub const T28: &str = "T28"; // Invalid BIC code length
628        pub const T29: &str = "T29"; // Invalid BIC code structure
629        pub const T40: &str = "T40"; // Invalid amount format
630        pub const T43: &str = "T43"; // Amount exceeds maximum digits
631        pub const T45: &str = "T45"; // Invalid identifier code format
632        pub const T50: &str = "T50"; // Invalid date format
633        pub const T52: &str = "T52"; // Invalid currency code
634        pub const T56: &str = "T56"; // Invalid structured address
635        pub const T73: &str = "T73"; // Invalid country code
636        // Additional T-series codes will be added as needed
637    }
638
639    /// C-Series: Conditional/Business Rules Error Codes (57 codes)
640    pub mod business {
641        pub const C02: &str = "C02"; // Currency code mismatch
642        pub const C03: &str = "C03"; // Amount format validation
643        pub const C08: &str = "C08"; // Commodity currency not allowed
644        pub const C81: &str = "C81"; // Conditional field dependency
645        // Additional C-series codes will be added as needed
646    }
647
648    /// D-Series: Data/Content Validation Error Codes (77 codes)
649    pub mod content {
650        pub const D17: &str = "D17"; // Field presence requirement
651        pub const D18: &str = "D18"; // Mutually exclusive placement
652        pub const D19: &str = "D19"; // IBAN mandatory for SEPA
653        pub const D20: &str = "D20"; // Field 71A presence rules
654        pub const D22: &str = "D22"; // Exchange rate dependency
655        pub const D49: &str = "D49"; // Field 33B mandatory for EU
656        pub const D50: &str = "D50"; // SHA charge restrictions
657        pub const D51: &str = "D51"; // Field 33B with charge fields
658        pub const D75: &str = "D75"; // Exchange rate mandatory
659        pub const D79: &str = "D79"; // Field 71G dependency
660        pub const D93: &str = "D93"; // Account restrictions by code
661        // Additional D-series codes will be added as needed
662    }
663
664    /// E-Series: Enhanced/Field Relation Validation Error Codes (86 codes)
665    pub mod relation {
666        pub const E01: &str = "E01"; // Instruction code restrictions
667        pub const E02: &str = "E02"; // Prohibited instruction codes
668        pub const E03: &str = "E03"; // Field option restrictions
669        pub const E04: &str = "E04"; // Party identifier requirements
670        pub const E05: &str = "E05"; // Field 54A option restrictions
671        pub const E06: &str = "E06"; // Multiple field dependency
672        pub const E07: &str = "E07"; // Field 55A option restrictions
673        pub const E09: &str = "E09"; // Party identifier mandatory
674        pub const E10: &str = "E10"; // Beneficiary account mandatory
675        pub const E13: &str = "E13"; // OUR charge restrictions
676        pub const E15: &str = "E15"; // BEN charge requirements
677        pub const E16: &str = "E16"; // Field restrictions with SPRI
678        pub const E17: &str = "E17"; // Clearing code requirements
679        pub const E18: &str = "E18"; // Account restrictions CHQB
680        pub const E44: &str = "E44"; // Instruction code dependencies
681        pub const E45: &str = "E45"; // Instruction code field dependencies
682        // Additional E-series codes will be added as needed
683    }
684
685    /// G-Series: General/Field Validation Error Codes (823 codes)
686    pub mod general {
687        pub const G001: &str = "G001"; // Field format violation
688        pub const G050: &str = "G050"; // Field content validation
689        pub const G100: &str = "G100"; // Sequence validation
690        // Additional G-series codes will be added as needed
691    }
692}
693
694/// Helper functions for creating SWIFT validation errors
695impl SwiftValidationError {
696    /// Create a T-series format validation error
697    pub fn format_error(
698        code: &str,
699        field: &str,
700        value: &str,
701        expected: &str,
702        message: &str,
703    ) -> Self {
704        SwiftValidationError::Format(Box::new(SwiftFormatError {
705            code: code.to_string(),
706            field: field.to_string(),
707            value: value.to_string(),
708            expected: expected.to_string(),
709            message: message.to_string(),
710            context: None,
711        }))
712    }
713
714    /// Create a C-series business rule validation error
715    pub fn business_error(
716        code: &str,
717        field: &str,
718        related_fields: Vec<String>,
719        message: &str,
720        rule_description: &str,
721    ) -> Self {
722        SwiftValidationError::Business(Box::new(SwiftBusinessError {
723            code: code.to_string(),
724            field: field.to_string(),
725            related_fields,
726            message: message.to_string(),
727            rule_description: rule_description.to_string(),
728            context: None,
729        }))
730    }
731
732    /// Create a D-series content validation error
733    pub fn content_error(
734        code: &str,
735        field: &str,
736        content: &str,
737        message: &str,
738        requirements: &str,
739    ) -> Self {
740        SwiftValidationError::Content(Box::new(SwiftContentError {
741            code: code.to_string(),
742            field: field.to_string(),
743            content: content.to_string(),
744            message: message.to_string(),
745            requirements: requirements.to_string(),
746            context: None,
747        }))
748    }
749
750    /// Create an E-series relation validation error
751    pub fn relation_error(
752        code: &str,
753        field: &str,
754        related_fields: Vec<String>,
755        message: &str,
756        rule_description: &str,
757    ) -> Self {
758        SwiftValidationError::Relation(Box::new(SwiftRelationError {
759            code: code.to_string(),
760            field: field.to_string(),
761            related_fields,
762            instruction_context: None,
763            message: message.to_string(),
764            rule_description: rule_description.to_string(),
765            context: None,
766        }))
767    }
768
769    /// Create a G-series general validation error
770    pub fn general_error(
771        code: &str,
772        field: &str,
773        value: &str,
774        message: &str,
775        category: Option<&str>,
776    ) -> Self {
777        SwiftValidationError::General(Box::new(SwiftGeneralError {
778            code: code.to_string(),
779            field: field.to_string(),
780            value: value.to_string(),
781            message: message.to_string(),
782            category: category.map(|s| s.to_string()),
783            context: None,
784        }))
785    }
786
787    /// Get the error code from any SWIFT validation error
788    pub fn code(&self) -> &str {
789        match self {
790            SwiftValidationError::Format(err) => &err.code,
791            SwiftValidationError::Business(err) => &err.code,
792            SwiftValidationError::Content(err) => &err.code,
793            SwiftValidationError::Relation(err) => &err.code,
794            SwiftValidationError::General(err) => &err.code,
795        }
796    }
797
798    /// Get the field tag from any SWIFT validation error
799    pub fn field(&self) -> &str {
800        match self {
801            SwiftValidationError::Format(err) => &err.field,
802            SwiftValidationError::Business(err) => &err.field,
803            SwiftValidationError::Content(err) => &err.field,
804            SwiftValidationError::Relation(err) => &err.field,
805            SwiftValidationError::General(err) => &err.field,
806        }
807    }
808
809    /// Get the error message from any SWIFT validation error
810    pub fn message(&self) -> &str {
811        match self {
812            SwiftValidationError::Format(err) => &err.message,
813            SwiftValidationError::Business(err) => &err.message,
814            SwiftValidationError::Content(err) => &err.message,
815            SwiftValidationError::Relation(err) => &err.message,
816            SwiftValidationError::General(err) => &err.message,
817        }
818    }
819}
820
821/// Convert SwiftValidationError to ValidationError for backward compatibility
822impl From<SwiftValidationError> for ValidationError {
823    fn from(swift_error: SwiftValidationError) -> Self {
824        match swift_error {
825            SwiftValidationError::Format(err) => ValidationError::FormatValidation {
826                field_tag: err.field,
827                message: format!("{}: {}", err.code, err.message),
828            },
829            SwiftValidationError::Business(err) => ValidationError::BusinessRuleValidation {
830                rule_name: err.code,
831                message: err.message,
832            },
833            SwiftValidationError::Content(err) => ValidationError::ValueValidation {
834                field_tag: err.field,
835                message: format!("{}: {}", err.code, err.message),
836            },
837            SwiftValidationError::Relation(err) => ValidationError::BusinessRuleValidation {
838                rule_name: err.code,
839                message: err.message,
840            },
841            SwiftValidationError::General(err) => ValidationError::FormatValidation {
842                field_tag: err.field,
843                message: format!("{}: {}", err.code, err.message),
844            },
845        }
846    }
847}
848
849/// Convert SwiftValidationError to ParseError
850impl From<SwiftValidationError> for ParseError {
851    fn from(validation_error: SwiftValidationError) -> Self {
852        ParseError::SwiftValidation(Box::new(validation_error))
853    }
854}
855
856/// Convert ValidationError to SwiftValidationError
857impl From<ValidationError> for SwiftValidationError {
858    fn from(validation_error: ValidationError) -> Self {
859        match validation_error {
860            ValidationError::FormatValidation { field_tag, message } => {
861                SwiftValidationError::format_error("T00", &field_tag, "", "", &message)
862            }
863            ValidationError::LengthValidation {
864                field_tag,
865                expected,
866                actual,
867            } => SwiftValidationError::format_error(
868                "T00",
869                &field_tag,
870                &actual.to_string(),
871                &expected,
872                "Length validation failed",
873            ),
874            ValidationError::PatternValidation { field_tag, message } => {
875                SwiftValidationError::format_error("T00", &field_tag, "", "", &message)
876            }
877            ValidationError::ValueValidation { field_tag, message } => {
878                SwiftValidationError::content_error("D00", &field_tag, "", &message, "")
879            }
880            ValidationError::BusinessRuleValidation { rule_name, message } => {
881                SwiftValidationError::business_error(&rule_name, "", vec![], &message, "")
882            }
883        }
884    }
885}