swift_mt_message/
swift_message.rs1use crate::{
6 ValidationError, ValidationResult,
7 headers::{ApplicationHeader, BasicHeader, Trailer, UserHeader},
8 traits::SwiftMessageBody,
9};
10use serde::{Deserialize, Serialize};
11use std::any::Any;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15#[cfg_attr(
16 feature = "jsonschema",
17 derive(schemars::JsonSchema),
18 schemars(bound = "T: schemars::JsonSchema")
19)]
20pub struct SwiftMessage<T: SwiftMessageBody> {
21 pub basic_header: BasicHeader,
23
24 pub application_header: ApplicationHeader,
26
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub user_header: Option<UserHeader>,
30
31 #[serde(skip_serializing_if = "Option::is_none")]
33 pub trailer: Option<Trailer>,
34
35 pub message_type: String,
37
38 pub fields: T,
40}
41
42impl<T: SwiftMessageBody> SwiftMessage<T> {
43 pub fn has_reject_codes(&self) -> bool {
45 if let Some(ref user_header) = self.user_header
47 && let Some(ref mur) = user_header.message_user_reference
48 && mur.to_uppercase().contains("REJT")
49 {
50 return true;
51 }
52
53 if let Some(mt103_fields) =
54 (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT103>()
55 {
56 return mt103_fields.has_reject_codes();
57 } else if let Some(mt202_fields) =
58 (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT202>()
59 {
60 return mt202_fields.has_reject_codes();
61 } else if let Some(mt205_fields) =
62 (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT205>()
63 {
64 return mt205_fields.has_reject_codes();
65 }
66
67 false
68 }
69
70 pub fn has_return_codes(&self) -> bool {
72 if let Some(ref user_header) = self.user_header
74 && let Some(ref mur) = user_header.message_user_reference
75 && mur.to_uppercase().contains("RETN")
76 {
77 return true;
78 }
79
80 if let Some(mt103_fields) =
81 (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT103>()
82 {
83 return mt103_fields.has_return_codes();
84 } else if let Some(mt202_fields) =
85 (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT202>()
86 {
87 return mt202_fields.has_return_codes();
88 } else if let Some(mt205_fields) =
89 (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT205>()
90 {
91 return mt205_fields.has_return_codes();
92 }
93
94 false
95 }
96
97 pub fn is_cover_message(&self) -> bool {
98 if let Some(mt202_fields) =
99 (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT202>()
100 {
101 return mt202_fields.is_cover_message();
102 }
103 if let Some(mt205_fields) =
104 (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT205>()
105 {
106 return mt205_fields.is_cover_message();
107 }
108
109 false
110 }
111
112 pub fn is_stp_message(&self) -> bool {
113 if let Some(mt103_fields) =
114 (&self.fields as &dyn Any).downcast_ref::<crate::messages::MT103>()
115 {
116 return mt103_fields.is_stp_compliant();
117 }
118
119 false
120 }
121
122 pub fn validate(&self) -> ValidationResult {
124 let validation_errors = self.fields.validate_network_rules(false);
126
127 let errors: Vec<ValidationError> = validation_errors
129 .into_iter()
130 .map(|swift_error| {
131 let message = format!("{}", swift_error);
132 ValidationError::BusinessRuleValidation {
133 rule_name: swift_error.error_code().to_string(),
134 message,
135 }
136 })
137 .collect();
138
139 ValidationResult {
140 is_valid: errors.is_empty(),
141 errors,
142 warnings: Vec::new(),
143 }
144 }
145
146 pub fn to_mt_message(&self) -> String {
147 let mut swift_message = String::with_capacity(2200);
150
151 let block1 = &self.basic_header.to_string();
153 swift_message.push_str(&format!("{{1:{block1}}}\n"));
154
155 let block2 = &self.application_header.to_string();
157 swift_message.push_str(&format!("{{2:{block2}}}\n"));
158
159 if let Some(ref user_header) = self.user_header {
161 let block3 = &user_header.to_string();
162 swift_message.push_str(&format!("{{3:{block3}}}\n"));
163 }
164
165 let mut block4_content = self.fields.to_mt_string();
168
169 if block4_content.contains("\r\n") {
171 block4_content = block4_content.replace("\r\n", "\n");
172 }
173
174 let block4 = if block4_content.starts_with('\n') {
176 block4_content
177 } else {
178 format!("\n{}", block4_content)
179 };
180
181 swift_message.push_str(&format!("{{4:{block4}\n-}}\n"));
182
183 if let Some(ref trailer) = self.trailer {
185 let block5 = &trailer.to_string();
186 swift_message.push_str(&format!("{{5:{block5}}}\n"));
187 }
188
189 swift_message
190 }
191}