swift_mt_message/messages/
mt910.rs1use crate::errors::SwiftValidationError;
2use crate::fields::*;
3use crate::parser::utils::*;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
13pub struct MT910 {
14 #[serde(rename = "20")]
16 pub field_20: Field20,
17
18 #[serde(rename = "21")]
20 pub field_21: Field21NoOption,
21
22 #[serde(rename = "25")]
24 pub field_25: Field25AccountIdentification,
25
26 #[serde(rename = "13D")]
28 pub field_13d: Option<Field13D>,
29
30 #[serde(rename = "32A")]
32 pub field_32a: Field32A,
33
34 #[serde(flatten)]
36 pub field_50: Option<Field50OrderingCustomerAFK>,
37
38 #[serde(flatten)]
40 pub field_52: Option<Field52OrderingInstitution>,
41
42 #[serde(flatten)]
44 pub field_56: Option<Field56Intermediary>,
45
46 #[serde(rename = "72")]
48 pub field_72: Option<Field72>,
49}
50
51impl MT910 {
52 pub fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
54 let mut parser = crate::parser::MessageParser::new(block4, "910");
55
56 let field_20 = parser.parse_field::<Field20>("20")?;
58 let field_21 = parser.parse_field::<Field21NoOption>("21")?;
59 let field_25 = parser.parse_field::<Field25AccountIdentification>("25")?;
60
61 let field_13d = parser.parse_optional_field::<Field13D>("13D")?;
63
64 let field_32a = parser.parse_field::<Field32A>("32A")?;
66
67 let field_50 = parser.parse_optional_variant_field::<Field50OrderingCustomerAFK>("50")?;
69 let field_52 = parser.parse_optional_variant_field::<Field52OrderingInstitution>("52")?;
70 let field_56 = parser.parse_optional_variant_field::<Field56Intermediary>("56")?;
71 let field_72 = parser.parse_optional_field::<Field72>("72")?;
72
73 verify_parser_complete(&parser)?;
75
76 Ok(Self {
77 field_20,
78 field_21,
79 field_25,
80 field_13d,
81 field_32a,
82 field_50,
83 field_52,
84 field_56,
85 field_72,
86 })
87 }
88
89 pub fn parse(input: &str) -> Result<Self, crate::errors::ParseError> {
91 let block4 = extract_block4(input)?;
92 Self::parse_from_block4(&block4)
93 }
94
95 pub fn to_mt_string(&self) -> String {
97 let mut result = String::new();
98
99 append_field(&mut result, &self.field_20);
100 append_field(&mut result, &self.field_21);
101 append_field(&mut result, &self.field_25);
102 append_optional_field(&mut result, &self.field_13d);
103 append_field(&mut result, &self.field_32a);
104 append_optional_field(&mut result, &self.field_50);
105 append_optional_field(&mut result, &self.field_52);
106 append_optional_field(&mut result, &self.field_56);
107 append_optional_field(&mut result, &self.field_72);
108
109 finalize_mt_string(result, false)
110 }
111
112 fn has_ordering_customer(&self) -> bool {
122 self.field_50.is_some()
123 }
124
125 fn has_ordering_institution(&self) -> bool {
127 self.field_52.is_some()
128 }
129
130 fn validate_c1_ordering_party(&self) -> Option<SwiftValidationError> {
137 let has_50 = self.has_ordering_customer();
138 let has_52 = self.has_ordering_institution();
139
140 if !has_50 && !has_52 {
141 return Some(SwiftValidationError::business_error(
142 "C06",
143 "50a/52a",
144 vec![],
145 "Either field 50a (Ordering Customer) or field 52a (Ordering Institution) must be present",
146 "At least one of fields 50a or 52a must be present in the message. If field 50a is present, field 52a is optional. If field 52a is present, field 50a is optional. Both fields cannot be absent",
147 ));
148 }
149
150 None
151 }
152
153 pub fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
156 let mut all_errors = Vec::new();
157
158 if let Some(error) = self.validate_c1_ordering_party() {
160 all_errors.push(error);
161 if stop_on_first_error {
162 return all_errors;
163 }
164 }
165
166 all_errors
167 }
168}
169
170impl crate::traits::SwiftMessageBody for MT910 {
171 fn message_type() -> &'static str {
172 "910"
173 }
174
175 fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
176 MT910::parse_from_block4(block4)
178 }
179
180 fn to_mt_string(&self) -> String {
181 MT910::to_mt_string(self)
183 }
184
185 fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
186 MT910::validate_network_rules(self, stop_on_first_error)
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn test_mt910_parse() {
197 let mt910_text = r#":20:20240719001
198:21:REF20240719001
199:25:12345678901234567890
200:32A:240719USD1000,00
201:50K:JOHN DOE
202123 MAIN STREET
203NEW YORK
204-"#;
205 let result = MT910::parse_from_block4(mt910_text);
206 assert!(result.is_ok());
207 let mt910 = result.unwrap();
208 assert_eq!(mt910.field_20.reference, "20240719001");
209 assert_eq!(mt910.field_21.reference, "REF20240719001");
210 }
211
212 #[test]
213 fn test_mt910_validation_c1_fails_without_ordering_party() {
214 let mt910_text = r#":20:20240719001
216:21:REF20240719001
217:25:12345678901234567890
218:32A:240719USD1000,00
219-"#;
220 let result = MT910::parse_from_block4(mt910_text);
221 assert!(result.is_ok());
222 let mt910 = result.unwrap();
223
224 let errors = mt910.validate_network_rules(false);
226 assert_eq!(errors.len(), 1);
227 assert_eq!(errors[0].code(), "C06");
228 assert!(errors[0].message().contains("Either field 50a"));
229 }
230
231 #[test]
232 fn test_mt910_validation_c1_passes_with_field_50() {
233 let mt910_text = r#":20:20240719001
235:21:REF20240719001
236:25:12345678901234567890
237:32A:240719USD1000,00
238:50K:JOHN DOE
239123 MAIN STREET
240NEW YORK
241-"#;
242 let result = MT910::parse_from_block4(mt910_text);
243 assert!(result.is_ok());
244 let mt910 = result.unwrap();
245
246 let errors = mt910.validate_network_rules(false);
248 assert_eq!(errors.len(), 0);
249 }
250
251 #[test]
252 fn test_mt910_validation_c1_passes_with_field_52() {
253 let mt910_text = r#":20:20240719001
255:21:REF20240719001
256:25:12345678901234567890
257:32A:240719USD1000,00
258:52A:DEUTDEFFXXX
259-"#;
260 let result = MT910::parse_from_block4(mt910_text);
261 if let Err(ref e) = result {
262 eprintln!("Parse error: {:?}", e);
263 }
264 assert!(result.is_ok());
265 let mt910 = result.unwrap();
266
267 let errors = mt910.validate_network_rules(false);
269 assert_eq!(errors.len(), 0);
270 }
271
272 #[test]
273 fn test_mt910_validation_c1_passes_with_both_fields() {
274 let mt910_text = r#":20:20240719001
276:21:REF20240719001
277:25:12345678901234567890
278:32A:240719USD1000,00
279:50K:JOHN DOE
280123 MAIN STREET
281NEW YORK
282:52A:DEUTDEFFXXX
283-"#;
284 let result = MT910::parse_from_block4(mt910_text);
285 assert!(result.is_ok());
286 let mt910 = result.unwrap();
287
288 let errors = mt910.validate_network_rules(false);
290 assert_eq!(errors.len(), 0);
291 }
292}