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 result.push('-');
110 result
111 }
112
113 fn has_ordering_customer(&self) -> bool {
123 self.field_50.is_some()
124 }
125
126 fn has_ordering_institution(&self) -> bool {
128 self.field_52.is_some()
129 }
130
131 fn validate_c1_ordering_party(&self) -> Option<SwiftValidationError> {
138 let has_50 = self.has_ordering_customer();
139 let has_52 = self.has_ordering_institution();
140
141 if !has_50 && !has_52 {
142 return Some(SwiftValidationError::business_error(
143 "C06",
144 "50a/52a",
145 vec![],
146 "Either field 50a (Ordering Customer) or field 52a (Ordering Institution) must be present",
147 "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",
148 ));
149 }
150
151 None
152 }
153
154 pub fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
157 let mut all_errors = Vec::new();
158
159 if let Some(error) = self.validate_c1_ordering_party() {
161 all_errors.push(error);
162 if stop_on_first_error {
163 return all_errors;
164 }
165 }
166
167 all_errors
168 }
169}
170
171impl crate::traits::SwiftMessageBody for MT910 {
172 fn message_type() -> &'static str {
173 "910"
174 }
175
176 fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
177 MT910::parse_from_block4(block4)
179 }
180
181 fn to_mt_string(&self) -> String {
182 MT910::to_mt_string(self)
184 }
185
186 fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
187 MT910::validate_network_rules(self, stop_on_first_error)
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 #[test]
197 fn test_mt910_parse() {
198 let mt910_text = r#":20:20240719001
199:21:REF20240719001
200:25:12345678901234567890
201:32A:240719USD1000,00
202:50K:JOHN DOE
203123 MAIN STREET
204NEW YORK
205-"#;
206 let result = MT910::parse_from_block4(mt910_text);
207 assert!(result.is_ok());
208 let mt910 = result.unwrap();
209 assert_eq!(mt910.field_20.reference, "20240719001");
210 assert_eq!(mt910.field_21.reference, "REF20240719001");
211 }
212
213 #[test]
214 fn test_mt910_validation_c1_fails_without_ordering_party() {
215 let mt910_text = r#":20:20240719001
217:21:REF20240719001
218:25:12345678901234567890
219:32A:240719USD1000,00
220-"#;
221 let result = MT910::parse_from_block4(mt910_text);
222 assert!(result.is_ok());
223 let mt910 = result.unwrap();
224
225 let errors = mt910.validate_network_rules(false);
227 assert_eq!(errors.len(), 1);
228 assert_eq!(errors[0].code(), "C06");
229 assert!(errors[0].message().contains("Either field 50a"));
230 }
231
232 #[test]
233 fn test_mt910_validation_c1_passes_with_field_50() {
234 let mt910_text = r#":20:20240719001
236:21:REF20240719001
237:25:12345678901234567890
238:32A:240719USD1000,00
239:50K:JOHN DOE
240123 MAIN STREET
241NEW YORK
242-"#;
243 let result = MT910::parse_from_block4(mt910_text);
244 assert!(result.is_ok());
245 let mt910 = result.unwrap();
246
247 let errors = mt910.validate_network_rules(false);
249 assert_eq!(errors.len(), 0);
250 }
251
252 #[test]
253 fn test_mt910_validation_c1_passes_with_field_52() {
254 let mt910_text = r#":20:20240719001
256:21:REF20240719001
257:25:12345678901234567890
258:32A:240719USD1000,00
259:52A:DEUTDEFFXXX
260-"#;
261 let result = MT910::parse_from_block4(mt910_text);
262 if let Err(ref e) = result {
263 eprintln!("Parse error: {:?}", e);
264 }
265 assert!(result.is_ok());
266 let mt910 = result.unwrap();
267
268 let errors = mt910.validate_network_rules(false);
270 assert_eq!(errors.len(), 0);
271 }
272
273 #[test]
274 fn test_mt910_validation_c1_passes_with_both_fields() {
275 let mt910_text = r#":20:20240719001
277:21:REF20240719001
278:25:12345678901234567890
279:32A:240719USD1000,00
280:50K:JOHN DOE
281123 MAIN STREET
282NEW YORK
283:52A:DEUTDEFFXXX
284-"#;
285 let result = MT910::parse_from_block4(mt910_text);
286 assert!(result.is_ok());
287 let mt910 = result.unwrap();
288
289 let errors = mt910.validate_network_rules(false);
291 assert_eq!(errors.len(), 0);
292 }
293}