swift_mt_message/
lib.rs

1//! # Swift MT Message Parser - Enhanced Architecture
2//!
3//! A comprehensive Rust library for parsing SWIFT MT (Message Type) messages with strong typing,
4//! complex field structures, comprehensive validation, and flattened JSON serialization.
5//!
6//! ## Key Features
7//!
8//! - **Complex Field Structures**: Full enum-based field variants (Field50: A/F/K, Field59: A/Basic)
9//! - **Flattened JSON Serialization**: Clean JSON output without enum wrapper layers
10//! - **Type-safe field parsing** with dedicated field structs and automatic validation
11//! - **Comprehensive Field Support**: All MT103 fields with proper SWIFT compliance
12//! - **Bidirectional Serialization**: Perfect round-trip JSON serialization/deserialization
13//! - **Extensive Validation**: BIC validation, field length checks, format compliance
14//!
15//! ## Supported Field Types
16//!
17//! ### Complex Enum Fields
18//! - **Field50** (Ordering Customer): 50A (Account+BIC), 50F (Party+Address), 50K (Name+Address)
19//! - **Field59** (Beneficiary Customer): 59A (Account+BIC), 59 (Basic lines)
20//!
21//! ### Institution Fields (with account_line_indicator)
22//! - **Field52A** (Ordering Institution): BIC + optional account + account_line_indicator
23//! - **Field53A-57A** (Correspondent/Intermediary): All with account_line_indicator support
24//!
25//! ### Simple Type Fields
26//! - **Field32A** (Value Date/Currency/Amount): NaiveDate + String + f64
27//! - **Field20, 23B, 70, 71A**: Proper field name alignment with old version
28//!
29//! ## JSON Output Structure
30//!
31//! The library produces clean, flattened JSON without enum wrapper layers:
32//!
33//! ```json
34//! {
35//!   "50": {
36//!     "name_and_address": ["JOHN DOE", "123 MAIN ST"]
37//!   },
38//!   "59": {
39//!     "account": "DE89370400440532013000",
40//!     "bic": "DEUTDEFFXXX"
41//!   }
42//! }
43//! ```
44//!
45//! Instead of nested enum structures like `{"50": {"K": {...}}}`.
46
47use serde::{Deserialize, Serialize};
48use std::any::Any;
49use std::collections::HashMap;
50use std::fmt::Debug;
51
52pub mod errors;
53pub mod fields;
54pub mod headers;
55pub mod messages;
56pub mod parser;
57pub mod sample;
58
59// Re-export core types
60pub use errors::{ParseError, Result, ValidationError};
61pub use headers::{ApplicationHeader, BasicHeader, Trailer, UserHeader};
62pub use parser::SwiftParser;
63
64// Re-export derive macros
65pub use swift_mt_message_macros::{SwiftField, SwiftMessage, field, serde_swift_fields};
66
67/// Simplified result type for SWIFT operations
68pub type SwiftResult<T> = std::result::Result<T, crate::errors::ParseError>;
69
70/// Core trait for all Swift field types
71pub trait SwiftField: Serialize + for<'de> Deserialize<'de> + Clone + std::fmt::Debug {
72    /// Parse field value from string representation
73    fn parse(value: &str) -> Result<Self>
74    where
75        Self: Sized;
76
77    /// Convert field back to SWIFT string format
78    fn to_swift_string(&self) -> String;
79
80    /// Validate field according to SWIFT format rules
81    fn validate(&self) -> ValidationResult;
82
83    /// Get field format specification
84    fn format_spec() -> &'static str;
85
86    /// Generate a random sample of this field
87    fn sample() -> Self
88    where
89        Self: Sized;
90
91    /// Generate a random sample with configuration
92    fn sample_with_config(config: &sample::FieldConfig) -> Self
93    where
94        Self: Sized;
95}
96
97/// Core trait for Swift message types
98pub trait SwiftMessageBody: Debug + Clone + Send + Sync + Serialize + std::any::Any {
99    /// Get the message type identifier (e.g., "103", "202")
100    fn message_type() -> &'static str;
101
102    /// Create from field map
103    fn from_fields(fields: HashMap<String, Vec<String>>) -> SwiftResult<Self>
104    where
105        Self: Sized;
106
107    /// Convert to field map
108    fn to_fields(&self) -> HashMap<String, Vec<String>>;
109
110    /// Get required field tags for this message type
111    fn required_fields() -> Vec<&'static str>;
112
113    /// Get optional field tags for this message type
114    fn optional_fields() -> Vec<&'static str>;
115
116    /// Generate a sample message with only mandatory fields
117    fn sample() -> Self
118    where
119        Self: Sized;
120
121    /// Generate a minimal sample (only mandatory fields)
122    fn sample_minimal() -> Self
123    where
124        Self: Sized;
125
126    /// Generate a full sample (all fields populated)
127    fn sample_full() -> Self
128    where
129        Self: Sized;
130
131    /// Generate a sample with configuration
132    fn sample_with_config(config: &sample::MessageConfig) -> Self
133    where
134        Self: Sized;
135}
136
137/// Complete SWIFT message with headers and body
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct SwiftMessage<T: SwiftMessageBody> {
140    /// Basic Header (Block 1)
141    pub basic_header: BasicHeader,
142
143    /// Application Header (Block 2)
144    pub application_header: ApplicationHeader,
145
146    /// User Header (Block 3) - Optional
147    #[serde(skip_serializing_if = "Option::is_none")]
148    pub user_header: Option<UserHeader>,
149
150    /// Trailer (Block 5) - Optional
151    #[serde(skip_serializing_if = "Option::is_none")]
152    pub trailer: Option<Trailer>,
153
154    /// Raw message blocks for preservation
155    pub blocks: Option<RawBlocks>,
156
157    /// Message type identifier
158    pub message_type: String,
159
160    /// Field order as they appeared in the original message
161    pub field_order: Vec<String>,
162
163    /// Parsed message body with typed fields
164    pub fields: T,
165}
166
167/// Raw message blocks for preservation and reconstruction
168#[derive(Debug, Clone, Serialize, Deserialize, Default)]
169pub struct RawBlocks {
170    #[serde(skip_serializing_if = "Option::is_none")]
171    pub block1: Option<String>,
172    #[serde(skip_serializing_if = "Option::is_none")]
173    pub block2: Option<String>,
174    #[serde(skip_serializing_if = "Option::is_none")]
175    pub block3: Option<String>,
176    #[serde(skip_serializing_if = "Option::is_none")]
177    pub block4: Option<String>,
178    #[serde(skip_serializing_if = "Option::is_none")]
179    pub block5: Option<String>,
180}
181
182/// Validation result for field and message validation
183#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct ValidationResult {
185    pub is_valid: bool,
186    pub errors: Vec<ValidationError>,
187    pub warnings: Vec<String>,
188}
189
190impl ValidationResult {
191    pub fn valid() -> Self {
192        Self {
193            is_valid: true,
194            errors: Vec::new(),
195            warnings: Vec::new(),
196        }
197    }
198
199    pub fn with_error(error: ValidationError) -> Self {
200        Self {
201            is_valid: false,
202            errors: vec![error],
203            warnings: Vec::new(),
204        }
205    }
206
207    pub fn with_errors(errors: Vec<ValidationError>) -> Self {
208        Self {
209            is_valid: errors.is_empty(),
210            errors,
211            warnings: Vec::new(),
212        }
213    }
214}
215
216/// Enumeration of all supported SWIFT message types for automatic parsing
217#[derive(Debug, Clone, Serialize, Deserialize)]
218#[serde(tag = "message_type")]
219pub enum ParsedSwiftMessage {
220    #[serde(rename = "101")]
221    MT101(Box<SwiftMessage<messages::MT101>>),
222    #[serde(rename = "103")]
223    MT103(Box<SwiftMessage<messages::MT103>>),
224    #[serde(rename = "104")]
225    MT104(Box<SwiftMessage<messages::MT104>>),
226    #[serde(rename = "107")]
227    MT107(Box<SwiftMessage<messages::MT107>>),
228    #[serde(rename = "110")]
229    MT110(Box<SwiftMessage<messages::MT110>>),
230    #[serde(rename = "111")]
231    MT111(Box<SwiftMessage<messages::MT111>>),
232    #[serde(rename = "112")]
233    MT112(Box<SwiftMessage<messages::MT112>>),
234    #[serde(rename = "202")]
235    MT202(Box<SwiftMessage<messages::MT202>>),
236    #[serde(rename = "205")]
237    MT205(Box<SwiftMessage<messages::MT205>>),
238    #[serde(rename = "210")]
239    MT210(Box<SwiftMessage<messages::MT210>>),
240    #[serde(rename = "900")]
241    MT900(Box<SwiftMessage<messages::MT900>>),
242    #[serde(rename = "910")]
243    MT910(Box<SwiftMessage<messages::MT910>>),
244    #[serde(rename = "920")]
245    MT920(Box<SwiftMessage<messages::MT920>>),
246    #[serde(rename = "935")]
247    MT935(Box<SwiftMessage<messages::MT935>>),
248    #[serde(rename = "940")]
249    MT940(Box<SwiftMessage<messages::MT940>>),
250    #[serde(rename = "941")]
251    MT941(Box<SwiftMessage<messages::MT941>>),
252    #[serde(rename = "942")]
253    MT942(Box<SwiftMessage<messages::MT942>>),
254    #[serde(rename = "950")]
255    MT950(Box<SwiftMessage<messages::MT950>>),
256    #[serde(rename = "192")]
257    MT192(Box<SwiftMessage<messages::MT192>>),
258    #[serde(rename = "196")]
259    MT196(Box<SwiftMessage<messages::MT196>>),
260    #[serde(rename = "292")]
261    MT292(Box<SwiftMessage<messages::MT292>>),
262    #[serde(rename = "296")]
263    MT296(Box<SwiftMessage<messages::MT296>>),
264}
265
266impl ParsedSwiftMessage {
267    /// Get the message type as a string
268    pub fn message_type(&self) -> &'static str {
269        match self {
270            ParsedSwiftMessage::MT101(_) => "101",
271            ParsedSwiftMessage::MT103(_) => "103",
272            ParsedSwiftMessage::MT104(_) => "104",
273            ParsedSwiftMessage::MT107(_) => "107",
274            ParsedSwiftMessage::MT110(_) => "110",
275            ParsedSwiftMessage::MT111(_) => "111",
276            ParsedSwiftMessage::MT112(_) => "112",
277            ParsedSwiftMessage::MT202(_) => "202",
278            ParsedSwiftMessage::MT205(_) => "205",
279            ParsedSwiftMessage::MT210(_) => "210",
280            ParsedSwiftMessage::MT900(_) => "900",
281            ParsedSwiftMessage::MT910(_) => "910",
282            ParsedSwiftMessage::MT920(_) => "920",
283            ParsedSwiftMessage::MT935(_) => "935",
284            ParsedSwiftMessage::MT940(_) => "940",
285            ParsedSwiftMessage::MT941(_) => "941",
286            ParsedSwiftMessage::MT942(_) => "942",
287            ParsedSwiftMessage::MT950(_) => "950",
288            ParsedSwiftMessage::MT192(_) => "192",
289            ParsedSwiftMessage::MT196(_) => "196",
290            ParsedSwiftMessage::MT292(_) => "292",
291            ParsedSwiftMessage::MT296(_) => "296",
292        }
293    }
294}
295
296impl ParsedSwiftMessage {
297    /// Convert to a specific message type if it matches
298    pub fn as_mt101(&self) -> Option<&SwiftMessage<messages::MT101>> {
299        match self {
300            ParsedSwiftMessage::MT101(msg) => Some(msg),
301            _ => None,
302        }
303    }
304    pub fn as_mt103(&self) -> Option<&SwiftMessage<messages::MT103>> {
305        match self {
306            ParsedSwiftMessage::MT103(msg) => Some(msg),
307            _ => None,
308        }
309    }
310    pub fn as_mt104(&self) -> Option<&SwiftMessage<messages::MT104>> {
311        match self {
312            ParsedSwiftMessage::MT104(msg) => Some(msg),
313            _ => None,
314        }
315    }
316    pub fn as_mt107(&self) -> Option<&SwiftMessage<messages::MT107>> {
317        match self {
318            ParsedSwiftMessage::MT107(msg) => Some(msg),
319            _ => None,
320        }
321    }
322    pub fn as_mt110(&self) -> Option<&SwiftMessage<messages::MT110>> {
323        match self {
324            ParsedSwiftMessage::MT110(msg) => Some(msg),
325            _ => None,
326        }
327    }
328    pub fn as_mt111(&self) -> Option<&SwiftMessage<messages::MT111>> {
329        match self {
330            ParsedSwiftMessage::MT111(msg) => Some(msg),
331            _ => None,
332        }
333    }
334    pub fn as_mt112(&self) -> Option<&SwiftMessage<messages::MT112>> {
335        match self {
336            ParsedSwiftMessage::MT112(msg) => Some(msg),
337            _ => None,
338        }
339    }
340    pub fn as_mt202(&self) -> Option<&SwiftMessage<messages::MT202>> {
341        match self {
342            ParsedSwiftMessage::MT202(msg) => Some(msg),
343            _ => None,
344        }
345    }
346    pub fn as_mt205(&self) -> Option<&SwiftMessage<messages::MT205>> {
347        match self {
348            ParsedSwiftMessage::MT205(msg) => Some(msg),
349            _ => None,
350        }
351    }
352    pub fn as_mt210(&self) -> Option<&SwiftMessage<messages::MT210>> {
353        match self {
354            ParsedSwiftMessage::MT210(msg) => Some(msg),
355            _ => None,
356        }
357    }
358    pub fn as_mt900(&self) -> Option<&SwiftMessage<messages::MT900>> {
359        match self {
360            ParsedSwiftMessage::MT900(msg) => Some(msg),
361            _ => None,
362        }
363    }
364    pub fn as_mt910(&self) -> Option<&SwiftMessage<messages::MT910>> {
365        match self {
366            ParsedSwiftMessage::MT910(msg) => Some(msg),
367            _ => None,
368        }
369    }
370    pub fn as_mt920(&self) -> Option<&SwiftMessage<messages::MT920>> {
371        match self {
372            ParsedSwiftMessage::MT920(msg) => Some(msg),
373            _ => None,
374        }
375    }
376    pub fn as_mt935(&self) -> Option<&SwiftMessage<messages::MT935>> {
377        match self {
378            ParsedSwiftMessage::MT935(msg) => Some(msg),
379            _ => None,
380        }
381    }
382    pub fn as_mt940(&self) -> Option<&SwiftMessage<messages::MT940>> {
383        match self {
384            ParsedSwiftMessage::MT940(msg) => Some(msg),
385            _ => None,
386        }
387    }
388    pub fn as_mt941(&self) -> Option<&SwiftMessage<messages::MT941>> {
389        match self {
390            ParsedSwiftMessage::MT941(msg) => Some(msg),
391            _ => None,
392        }
393    }
394    pub fn as_mt942(&self) -> Option<&SwiftMessage<messages::MT942>> {
395        match self {
396            ParsedSwiftMessage::MT942(msg) => Some(msg),
397            _ => None,
398        }
399    }
400    pub fn as_mt950(&self) -> Option<&SwiftMessage<messages::MT950>> {
401        match self {
402            ParsedSwiftMessage::MT950(msg) => Some(msg),
403            _ => None,
404        }
405    }
406    pub fn as_mt192(&self) -> Option<&SwiftMessage<messages::MT192>> {
407        match self {
408            ParsedSwiftMessage::MT192(msg) => Some(msg),
409            _ => None,
410        }
411    }
412    pub fn as_mt196(&self) -> Option<&SwiftMessage<messages::MT196>> {
413        match self {
414            ParsedSwiftMessage::MT196(msg) => Some(msg),
415            _ => None,
416        }
417    }
418    pub fn as_mt292(&self) -> Option<&SwiftMessage<messages::MT292>> {
419        match self {
420            ParsedSwiftMessage::MT292(msg) => Some(msg),
421            _ => None,
422        }
423    }
424    pub fn as_mt296(&self) -> Option<&SwiftMessage<messages::MT296>> {
425        match self {
426            ParsedSwiftMessage::MT296(msg) => Some(msg),
427            _ => None,
428        }
429    }
430
431    /// Convert into a specific message type if it matches
432    pub fn into_mt101(self) -> Option<SwiftMessage<messages::MT101>> {
433        match self {
434            ParsedSwiftMessage::MT101(msg) => Some(*msg),
435            _ => None,
436        }
437    }
438    pub fn into_mt103(self) -> Option<SwiftMessage<messages::MT103>> {
439        match self {
440            ParsedSwiftMessage::MT103(msg) => Some(*msg),
441            _ => None,
442        }
443    }
444    pub fn into_mt104(self) -> Option<SwiftMessage<messages::MT104>> {
445        match self {
446            ParsedSwiftMessage::MT104(msg) => Some(*msg),
447            _ => None,
448        }
449    }
450    pub fn into_mt107(self) -> Option<SwiftMessage<messages::MT107>> {
451        match self {
452            ParsedSwiftMessage::MT107(msg) => Some(*msg),
453            _ => None,
454        }
455    }
456    pub fn into_mt110(self) -> Option<SwiftMessage<messages::MT110>> {
457        match self {
458            ParsedSwiftMessage::MT110(msg) => Some(*msg),
459            _ => None,
460        }
461    }
462    pub fn into_mt111(self) -> Option<SwiftMessage<messages::MT111>> {
463        match self {
464            ParsedSwiftMessage::MT111(msg) => Some(*msg),
465            _ => None,
466        }
467    }
468    pub fn into_mt112(self) -> Option<SwiftMessage<messages::MT112>> {
469        match self {
470            ParsedSwiftMessage::MT112(msg) => Some(*msg),
471            _ => None,
472        }
473    }
474    pub fn into_mt202(self) -> Option<SwiftMessage<messages::MT202>> {
475        match self {
476            ParsedSwiftMessage::MT202(msg) => Some(*msg),
477            _ => None,
478        }
479    }
480    pub fn into_mt205(self) -> Option<SwiftMessage<messages::MT205>> {
481        match self {
482            ParsedSwiftMessage::MT205(msg) => Some(*msg),
483            _ => None,
484        }
485    }
486    pub fn into_mt210(self) -> Option<SwiftMessage<messages::MT210>> {
487        match self {
488            ParsedSwiftMessage::MT210(msg) => Some(*msg),
489            _ => None,
490        }
491    }
492    pub fn into_mt900(self) -> Option<SwiftMessage<messages::MT900>> {
493        match self {
494            ParsedSwiftMessage::MT900(msg) => Some(*msg),
495            _ => None,
496        }
497    }
498    pub fn into_mt910(self) -> Option<SwiftMessage<messages::MT910>> {
499        match self {
500            ParsedSwiftMessage::MT910(msg) => Some(*msg),
501            _ => None,
502        }
503    }
504    pub fn into_mt920(self) -> Option<SwiftMessage<messages::MT920>> {
505        match self {
506            ParsedSwiftMessage::MT920(msg) => Some(*msg),
507            _ => None,
508        }
509    }
510    pub fn into_mt935(self) -> Option<SwiftMessage<messages::MT935>> {
511        match self {
512            ParsedSwiftMessage::MT935(msg) => Some(*msg),
513            _ => None,
514        }
515    }
516    pub fn into_mt940(self) -> Option<SwiftMessage<messages::MT940>> {
517        match self {
518            ParsedSwiftMessage::MT940(msg) => Some(*msg),
519            _ => None,
520        }
521    }
522    pub fn into_mt941(self) -> Option<SwiftMessage<messages::MT941>> {
523        match self {
524            ParsedSwiftMessage::MT941(msg) => Some(*msg),
525            _ => None,
526        }
527    }
528    pub fn into_mt942(self) -> Option<SwiftMessage<messages::MT942>> {
529        match self {
530            ParsedSwiftMessage::MT942(msg) => Some(*msg),
531            _ => None,
532        }
533    }
534    pub fn into_mt950(self) -> Option<SwiftMessage<messages::MT950>> {
535        match self {
536            ParsedSwiftMessage::MT950(msg) => Some(*msg),
537            _ => None,
538        }
539    }
540    pub fn into_mt192(self) -> Option<SwiftMessage<messages::MT192>> {
541        match self {
542            ParsedSwiftMessage::MT192(msg) => Some(*msg),
543            _ => None,
544        }
545    }
546    pub fn into_mt196(self) -> Option<SwiftMessage<messages::MT196>> {
547        match self {
548            ParsedSwiftMessage::MT196(msg) => Some(*msg),
549            _ => None,
550        }
551    }
552    pub fn into_mt292(self) -> Option<SwiftMessage<messages::MT292>> {
553        match self {
554            ParsedSwiftMessage::MT292(msg) => Some(*msg),
555            _ => None,
556        }
557    }
558    pub fn into_mt296(self) -> Option<SwiftMessage<messages::MT296>> {
559        match self {
560            ParsedSwiftMessage::MT296(msg) => Some(*msg),
561            _ => None,
562        }
563    }
564}
565
566impl<T: SwiftMessageBody> SwiftMessage<T> {
567    /// Check if this message contains reject codes (MT103 specific)
568    ///
569    /// Reject messages are identified by checking:
570    /// 1. Field 20 (Sender's Reference) for "REJT" prefix
571    /// 2. Block 3 field 108 (MUR - Message User Reference) for "REJT"
572    /// 3. Field 72 (Sender to Receiver Information) containing `/REJT/` code
573    pub fn has_reject_codes(&self) -> bool {
574        // Check Block 3 field 108 (MUR - Message User Reference)
575        if let Some(ref user_header) = self.user_header {
576            if let Some(ref mur) = user_header.message_user_reference {
577                if mur.to_uppercase().contains("REJT") {
578                    return true;
579                }
580            }
581        }
582
583        if let Some(mt103_fields) =
584            (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT103>()
585        {
586            return mt103_fields.has_reject_codes();
587        } else if let Some(mt202_fields) =
588            (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT202>()
589        {
590            return mt202_fields.has_reject_codes();
591        } else if let Some(mt205_fields) =
592            (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT205>()
593        {
594            return mt205_fields.has_reject_codes();
595        }
596
597        false
598    }
599
600    /// Check if this message contains return codes (MT103 specific)
601    ///
602    /// Return messages are identified by checking:
603    /// 1. Field 20 (Sender's Reference) for "RETN" prefix
604    /// 2. Block 3 field 108 (MUR - Message User Reference) for "RETN"
605    /// 3. Field 72 (Sender to Receiver Information) containing `/RETN/` code
606    pub fn has_return_codes(&self) -> bool {
607        // Check Block 3 field 108 (MUR - Message User Reference)
608        if let Some(ref user_header) = self.user_header {
609            if let Some(ref mur) = user_header.message_user_reference {
610                if mur.to_uppercase().contains("RETN") {
611                    return true;
612                }
613            }
614        }
615
616        if let Some(mt103_fields) =
617            (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT103>()
618        {
619            return mt103_fields.has_return_codes();
620        } else if let Some(mt202_fields) =
621            (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT202>()
622        {
623            return mt202_fields.has_return_codes();
624        } else if let Some(mt205_fields) =
625            (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT205>()
626        {
627            return mt205_fields.has_return_codes();
628        }
629
630        false
631    }
632
633    pub fn is_cover_message(&self) -> bool {
634        if let Some(mt202_fields) =
635            (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT202>()
636        {
637            return mt202_fields.is_cover_message();
638        }
639        if let Some(mt205_fields) =
640            (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT205>()
641        {
642            return mt205_fields.is_cover_message();
643        }
644
645        false
646    }
647
648    pub fn is_stp_message(&self) -> bool {
649        if let Some(mt103_fields) =
650            (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT103>()
651        {
652            return mt103_fields.is_stp_compliant();
653        }
654
655        false
656    }
657
658    /// Validate message against business rules using JSONLogic
659    /// This validation method has access to both headers and message fields,
660    /// allowing for comprehensive validation of MT103 and other message types.
661    pub fn validate_business_rules(&self) -> ValidationResult {
662        // Check if the message type has validation rules
663        let validation_rules = match T::message_type() {
664            "101" => messages::MT101::validation_rules(),
665            "103" => messages::MT103::validation_rules(),
666            "104" => messages::MT104::validation_rules(),
667            "107" => messages::MT107::validation_rules(),
668            "110" => messages::MT110::validation_rules(),
669            "111" => messages::MT111::validation_rules(),
670            "112" => messages::MT112::validation_rules(),
671            "202" => messages::MT202::validation_rules(),
672            "205" => messages::MT205::validation_rules(),
673            "210" => messages::MT210::validation_rules(),
674            "900" => messages::MT900::validation_rules(),
675            "910" => messages::MT910::validation_rules(),
676            "920" => messages::MT920::validation_rules(),
677            "935" => messages::MT935::validation_rules(),
678            "940" => messages::MT940::validation_rules(),
679            "941" => messages::MT941::validation_rules(),
680            "942" => messages::MT942::validation_rules(),
681            "950" => messages::MT950::validation_rules(),
682            "192" => messages::MT192::validation_rules(),
683            "196" => messages::MT196::validation_rules(),
684            "292" => messages::MT292::validation_rules(),
685            "296" => messages::MT296::validation_rules(),
686            _ => {
687                return ValidationResult::with_error(ValidationError::BusinessRuleValidation {
688                    rule_name: "UNSUPPORTED_MESSAGE_TYPE".to_string(),
689                    message: format!(
690                        "No validation rules defined for message type {}",
691                        T::message_type()
692                    ),
693                });
694            }
695        };
696
697        // Parse the validation rules JSON
698        let rules_json: serde_json::Value = match serde_json::from_str(validation_rules) {
699            Ok(json) => json,
700            Err(e) => {
701                return ValidationResult::with_error(ValidationError::BusinessRuleValidation {
702                    rule_name: "JSON_PARSE".to_string(),
703                    message: format!("Failed to parse validation rules JSON: {e}"),
704                });
705            }
706        };
707
708        // Extract rules array from the JSON
709        let rules = match rules_json.get("rules").and_then(|r| r.as_array()) {
710            Some(rules) => rules,
711            None => {
712                return ValidationResult::with_error(ValidationError::BusinessRuleValidation {
713                    rule_name: "RULES_FORMAT".to_string(),
714                    message: "Validation rules must contain a 'rules' array".to_string(),
715                });
716            }
717        };
718
719        // Get constants if they exist
720        let constants = rules_json
721            .get("constants")
722            .and_then(|c| c.as_object())
723            .cloned()
724            .unwrap_or_default();
725
726        // Create comprehensive data context with headers and fields
727        let context_value = match self.create_validation_context(&constants) {
728            Ok(context) => context,
729            Err(e) => {
730                return ValidationResult::with_error(ValidationError::BusinessRuleValidation {
731                    rule_name: "CONTEXT_CREATION".to_string(),
732                    message: format!("Failed to create validation context: {e}"),
733                });
734            }
735        };
736
737        // Validate each rule using datalogic-rs
738        let mut errors = Vec::new();
739        let mut warnings = Vec::new();
740
741        for (rule_index, rule) in rules.iter().enumerate() {
742            let rule_id = rule
743                .get("id")
744                .and_then(|id| id.as_str())
745                .map(|s| s.to_string())
746                .unwrap_or_else(|| format!("RULE_{rule_index}"));
747
748            let rule_description = rule
749                .get("description")
750                .and_then(|desc| desc.as_str())
751                .unwrap_or("No description");
752
753            if let Some(condition) = rule.get("condition") {
754                // Create DataLogic instance for evaluation
755                let dl = datalogic_rs::DataLogic::new();
756                match dl.evaluate_json(condition, &context_value, None) {
757                    Ok(result) => {
758                        match result.as_bool() {
759                            Some(true) => {
760                                // Rule passed
761                                continue;
762                            }
763                            Some(false) => {
764                                // Rule failed
765                                errors.push(ValidationError::BusinessRuleValidation {
766                                    rule_name: rule_id.clone(),
767                                    message: format!(
768                                        "Business rule validation failed: {rule_id} - {rule_description}"
769                                    ),
770                                });
771                            }
772                            None => {
773                                // Rule returned non-boolean value
774                                warnings.push(format!(
775                                    "Rule {rule_id} returned non-boolean value: {result:?}"
776                                ));
777                            }
778                        }
779                    }
780                    Err(e) => {
781                        // JSONLogic evaluation error
782                        errors.push(ValidationError::BusinessRuleValidation {
783                            rule_name: rule_id.clone(),
784                            message: format!("JSONLogic evaluation error for rule {rule_id}: {e}"),
785                        });
786                    }
787                }
788            } else {
789                warnings.push(format!("Rule {rule_id} has no condition"));
790            }
791        }
792
793        ValidationResult {
794            is_valid: errors.is_empty(),
795            errors,
796            warnings,
797        }
798    }
799
800    /// Create a comprehensive validation context that includes headers, fields, and constants
801    fn create_validation_context(
802        &self,
803        constants: &serde_json::Map<String, serde_json::Value>,
804    ) -> Result<serde_json::Value> {
805        // Serialize the entire message (including headers) to JSON for data context
806        let full_message_data = match serde_json::to_value(self) {
807            Ok(data) => data,
808            Err(e) => {
809                return Err(ParseError::SerializationError {
810                    message: format!("Failed to serialize complete message: {e}"),
811                });
812            }
813        };
814
815        // Create a comprehensive data context
816        let mut data_context = serde_json::Map::new();
817
818        // Add the complete message data
819        if let serde_json::Value::Object(msg_obj) = full_message_data {
820            for (key, value) in msg_obj {
821                data_context.insert(key, value);
822            }
823        }
824
825        // Add constants to data context
826        for (key, value) in constants {
827            data_context.insert(key.clone(), value.clone());
828        }
829
830        // Extract sender and receiver BIC from headers for enhanced validation context
831        let (sender_country, receiver_country) = self.extract_country_codes_from_bics();
832
833        // Add enhanced message context including BIC-derived information
834        data_context.insert("message_context".to_string(), serde_json::json!({
835            "message_type": self.message_type,
836            "sender_country": sender_country,
837            "receiver_country": receiver_country,
838            "sender_bic": self.basic_header.logical_terminal,
839            "receiver_bic": &self.application_header.destination_address,
840            "message_priority": &self.application_header.priority,
841            "delivery_monitoring": self.application_header.delivery_monitoring.as_ref().unwrap_or(&"3".to_string()),
842        }));
843
844        Ok(serde_json::Value::Object(data_context))
845    }
846
847    /// Extract country codes from BIC codes in the headers
848    fn extract_country_codes_from_bics(&self) -> (String, String) {
849        // Extract sender country from basic header BIC (positions 4-5)
850        let sender_country = if self.basic_header.logical_terminal.len() >= 6 {
851            self.basic_header.logical_terminal[4..6].to_string()
852        } else {
853            "XX".to_string() // Unknown country
854        };
855
856        // Extract receiver country from application header destination BIC
857        let receiver_country = if self.application_header.destination_address.len() >= 6 {
858            self.application_header.destination_address[4..6].to_string()
859        } else {
860            "XX".to_string()
861        };
862
863        (sender_country, receiver_country)
864    }
865
866    pub fn to_mt_message(&self) -> String {
867        let mut swift_message = String::new();
868
869        // Block 1: Basic Header
870        let block1 = &self.basic_header.to_string();
871        swift_message.push_str(&format!("{{1:{block1}}}\n"));
872
873        // Block 2: Application Header
874        let block2 = &self.application_header.to_string();
875        swift_message.push_str(&format!("{{2:{block2}}}\n"));
876
877        // Block 3: User Header (if present)
878        if let Some(ref user_header) = self.user_header {
879            let block3 = &user_header.to_string();
880            swift_message.push_str(&format!("{{3:{block3}}}\n"));
881        }
882
883        // Block 4: Text Block with fields
884        let field_map = self.fields.to_fields();
885        let mut block4 = String::new();
886
887        // Get optional field tags for this message type to determine which fields can be skipped
888        let optional_fields: std::collections::HashSet<String> = T::optional_fields()
889            .into_iter()
890            .map(|s| s.to_string())
891            .collect();
892
893        // Use field_order to maintain proper field sequence
894        for field_tag in &self.field_order {
895            if let Some(field_values) = field_map.get(field_tag) {
896                for field_value in field_values {
897                    // Skip empty optional fields
898                    if optional_fields.contains(field_tag) && field_value.trim().is_empty() {
899                        continue;
900                    }
901
902                    // field_value already includes the field tag prefix from to_swift_string()
903                    // but we need to check if it starts with ':' to avoid double prefixing
904                    if field_value.starts_with(':') {
905                        // Value already has field tag prefix, use as-is
906                        block4.push_str(&format!("\n{field_value}"));
907                    } else {
908                        // Value doesn't have field tag prefix, add it
909                        block4.push_str(&format!("\n:{field_tag}:{field_value}"));
910                    }
911                }
912            }
913        }
914
915        // Handle any fields not in field_order (shouldn't happen in normal cases)
916        for (field_tag, field_values) in &field_map {
917            if !self.field_order.contains(field_tag) {
918                for field_value in field_values {
919                    // Skip empty optional fields
920                    if optional_fields.contains(field_tag) && field_value.trim().is_empty() {
921                        continue;
922                    }
923
924                    if field_value.starts_with(':') {
925                        block4.push_str(&format!("\n{field_value}"));
926                    } else {
927                        block4.push_str(&format!("\n:{field_tag}:{field_value}"));
928                    }
929                }
930            }
931        }
932
933        swift_message.push_str(&format!("{{4:{block4}\n-}}"));
934        swift_message.push('\n');
935
936        // Block 5: Trailer (if present)
937        if let Some(ref trailer) = self.trailer {
938            let block5 = &trailer.to_string();
939            swift_message.push_str(&format!("{{5:{block5}}}\n"));
940        }
941
942        swift_message
943    }
944
945    /// Generate a sample SWIFT message with headers and message body
946    /// Returns a complete message with all blocks including sample headers
947    pub fn sample() -> Self
948    where
949        T: SwiftMessageBody,
950    {
951        Self::sample_with_config(&sample::MessageConfig::default())
952    }
953
954    /// Generate a minimal sample SWIFT message (mandatory fields only)
955    /// Returns a complete message with headers and minimal field set
956    pub fn sample_minimal() -> Self
957    where
958        T: SwiftMessageBody,
959    {
960        let config = sample::MessageConfig {
961            scenario: Some(sample::MessageScenario::Minimal),
962            ..Default::default()
963        };
964        Self::sample_with_config(&config)
965    }
966
967    /// Generate a full sample SWIFT message (all fields populated)
968    /// Returns a complete message with headers and all possible fields
969    pub fn sample_full() -> Self
970    where
971        T: SwiftMessageBody,
972    {
973        let config = sample::MessageConfig {
974            scenario: Some(sample::MessageScenario::Full),
975            include_optional: true,
976            ..Default::default()
977        };
978        Self::sample_with_config(&config)
979    }
980
981    /// Generate a sample SWIFT message with custom configuration
982    /// Returns a complete message with headers and configurable field generation
983    pub fn sample_with_config(config: &sample::MessageConfig) -> Self
984    where
985        T: SwiftMessageBody,
986    {
987        use rand::Rng;
988        let mut rng = rand::thread_rng();
989
990        // Generate sample message body based on configuration
991        let fields = match config.scenario {
992            Some(sample::MessageScenario::Minimal) => T::sample_minimal(),
993            Some(sample::MessageScenario::Full) => T::sample_full(),
994            _ => T::sample_with_config(config),
995        };
996
997        // Generate sample headers using the header module functions
998        let basic_header = BasicHeader::sample();
999        let application_header = ApplicationHeader::sample(T::message_type());
1000
1001        // Generate user header with UETR for CBPR+ compliance - always include for CBPR+
1002        let user_header = Some(UserHeader::sample_with_scenario(config.scenario.as_ref()));
1003
1004        // Generate optional trailer (Block 5) - include sometimes for realism
1005        let trailer = if config.scenario == Some(sample::MessageScenario::Full) || rng.gen_bool(0.2)
1006        {
1007            Some(Trailer::sample())
1008        } else {
1009            None
1010        };
1011
1012        // Generate field order based on required and optional fields
1013        let mut field_order = T::required_fields()
1014            .iter()
1015            .map(|s| s.to_string())
1016            .collect::<Vec<_>>();
1017
1018        if config.include_optional || config.scenario == Some(sample::MessageScenario::Full) {
1019            let mut optional_fields = T::optional_fields()
1020                .iter()
1021                .map(|s| s.to_string())
1022                .collect::<Vec<_>>();
1023            field_order.append(&mut optional_fields);
1024        }
1025
1026        // Sort field order numerically for proper SWIFT message structure
1027        field_order.sort_by(|a, b| {
1028            let a_num: u32 = a
1029                .chars()
1030                .take_while(|c| c.is_ascii_digit())
1031                .collect::<String>()
1032                .parse()
1033                .unwrap_or(0);
1034            let b_num: u32 = b
1035                .chars()
1036                .take_while(|c| c.is_ascii_digit())
1037                .collect::<String>()
1038                .parse()
1039                .unwrap_or(0);
1040            a_num.cmp(&b_num)
1041        });
1042
1043        SwiftMessage {
1044            basic_header,
1045            application_header,
1046            user_header,
1047            trailer,
1048            blocks: None,
1049            message_type: T::message_type().to_string(),
1050            field_order,
1051            fields,
1052        }
1053    }
1054}