swift_mt_message/fields/
field55.rs1use super::field_utils::{parse_name_and_address, parse_party_identifier};
2use super::swift_utils::{parse_bic, parse_max_length};
3use crate::errors::ParseError;
4use crate::traits::SwiftField;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12pub struct Field55A {
13 pub party_identifier: Option<String>,
15
16 pub bic: String,
18}
19
20impl SwiftField for Field55A {
21 fn parse(input: &str) -> crate::Result<Self>
22 where
23 Self: Sized,
24 {
25 let lines: Vec<&str> = input.split('\n').collect();
26
27 if lines.is_empty() {
28 return Err(ParseError::InvalidFormat {
29 message: "Field 55A requires input".to_string(),
30 });
31 }
32
33 let mut line_idx = 0;
34 let mut party_identifier = None;
35
36 if let Some(party_id) = parse_party_identifier(lines[0])? {
38 party_identifier = Some(format!("/{}", party_id));
39 line_idx = 1;
40 }
41
42 if line_idx >= lines.len() {
44 return Err(ParseError::InvalidFormat {
45 message: "Field 55A requires BIC code after party identifier".to_string(),
46 });
47 }
48
49 let bic = parse_bic(lines[line_idx])?;
51
52 Ok(Field55A {
53 party_identifier,
54 bic,
55 })
56 }
57
58 fn to_swift_string(&self) -> String {
59 let mut result = String::from(":55A:");
60 if let Some(ref party_id) = self.party_identifier {
61 result.push_str(party_id);
62 result.push('\n');
63 }
64 result.push_str(&self.bic);
65 result
66 }
67}
68
69#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
74pub struct Field55B {
75 pub party_identifier: Option<String>,
77
78 pub location: Option<String>,
80}
81
82impl SwiftField for Field55B {
83 fn parse(input: &str) -> crate::Result<Self>
84 where
85 Self: Sized,
86 {
87 if input.is_empty() {
88 return Ok(Field55B {
89 party_identifier: None,
90 location: None,
91 });
92 }
93
94 let lines: Vec<&str> = input.split('\n').collect();
95 let mut party_identifier = None;
96 let mut location = None;
97 let mut line_idx = 0;
98
99 if !lines.is_empty() && lines[0].starts_with('/') {
101 party_identifier = Some(lines[0].to_string());
102 line_idx = 1;
103 }
104
105 if line_idx < lines.len() && !lines[line_idx].is_empty() {
107 location = Some(parse_max_length(lines[line_idx], 35, "Field55B location")?);
108 }
109
110 Ok(Field55B {
111 party_identifier,
112 location,
113 })
114 }
115
116 fn to_swift_string(&self) -> String {
117 let mut result = String::from(":55B:");
118 if let Some(ref party_id) = self.party_identifier {
119 result.push_str(party_id);
120 if self.location.is_some() {
121 result.push('\n');
122 }
123 }
124 if let Some(ref loc) = self.location {
125 result.push_str(loc);
126 }
127 result
128 }
129}
130
131#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
136pub struct Field55D {
137 pub party_identifier: Option<String>,
139
140 pub name_and_address: Vec<String>,
142}
143
144impl SwiftField for Field55D {
145 fn parse(input: &str) -> crate::Result<Self>
146 where
147 Self: Sized,
148 {
149 let lines: Vec<&str> = input.split('\n').collect();
150
151 if lines.is_empty() {
152 return Err(ParseError::InvalidFormat {
153 message: "Field 55D requires at least one line".to_string(),
154 });
155 }
156
157 let mut party_identifier = None;
158 let mut start_idx = 0;
159
160 if let Some(party_id) = parse_party_identifier(lines[0])? {
162 party_identifier = Some(format!("/{}", party_id));
163 start_idx = 1;
164 }
165
166 let name_and_address = parse_name_and_address(&lines, start_idx, "Field55D")?;
168
169 Ok(Field55D {
170 party_identifier,
171 name_and_address,
172 })
173 }
174
175 fn to_swift_string(&self) -> String {
176 let mut result = String::from(":55D:");
177 if let Some(ref party_id) = self.party_identifier {
178 result.push_str(party_id);
179 result.push('\n');
180 }
181 for (i, line) in self.name_and_address.iter().enumerate() {
182 if i > 0 {
183 result.push('\n');
184 }
185 result.push_str(line);
186 }
187 result
188 }
189}
190
191#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
192pub enum Field55ThirdReimbursementInstitution {
193 #[serde(rename = "55A")]
194 A(Field55A),
195 #[serde(rename = "55B")]
196 B(Field55B),
197 #[serde(rename = "55D")]
198 D(Field55D),
199}
200
201impl SwiftField for Field55ThirdReimbursementInstitution {
202 fn parse(input: &str) -> crate::Result<Self>
203 where
204 Self: Sized,
205 {
206 let lines: Vec<&str> = input.split('\n').collect();
212 let last_line = lines.last().unwrap_or(&"");
213
214 if (8..=11).contains(&last_line.len())
216 && last_line
217 .chars()
218 .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit())
219 {
220 if let Ok(field) = Field55A::parse(input) {
222 return Ok(Field55ThirdReimbursementInstitution::A(field));
223 }
224 }
225
226 if lines.len() > 2 || (lines.len() == 2 && !lines[0].starts_with('/')) {
228 if let Ok(field) = Field55D::parse(input) {
230 return Ok(Field55ThirdReimbursementInstitution::D(field));
231 }
232 }
233
234 if let Ok(field) = Field55B::parse(input) {
236 return Ok(Field55ThirdReimbursementInstitution::B(field));
237 }
238
239 if let Ok(field) = Field55A::parse(input) {
241 return Ok(Field55ThirdReimbursementInstitution::A(field));
242 }
243 if let Ok(field) = Field55D::parse(input) {
244 return Ok(Field55ThirdReimbursementInstitution::D(field));
245 }
246
247 Err(ParseError::InvalidFormat {
248 message: "Field 55 could not be parsed as any valid option (A, B, or D)".to_string(),
249 })
250 }
251
252 fn parse_with_variant(
253 value: &str,
254 variant: Option<&str>,
255 _field_tag: Option<&str>,
256 ) -> crate::Result<Self>
257 where
258 Self: Sized,
259 {
260 match variant {
261 Some("A") => {
262 let field = Field55A::parse(value)?;
263 Ok(Field55ThirdReimbursementInstitution::A(field))
264 }
265 Some("B") => {
266 let field = Field55B::parse(value)?;
267 Ok(Field55ThirdReimbursementInstitution::B(field))
268 }
269 Some("D") => {
270 let field = Field55D::parse(value)?;
271 Ok(Field55ThirdReimbursementInstitution::D(field))
272 }
273 _ => {
274 Self::parse(value)
276 }
277 }
278 }
279
280 fn to_swift_string(&self) -> String {
281 match self {
282 Field55ThirdReimbursementInstitution::A(field) => field.to_swift_string(),
283 Field55ThirdReimbursementInstitution::B(field) => field.to_swift_string(),
284 Field55ThirdReimbursementInstitution::D(field) => field.to_swift_string(),
285 }
286 }
287
288 fn get_variant_tag(&self) -> Option<&'static str> {
289 match self {
290 Field55ThirdReimbursementInstitution::A(_) => Some("A"),
291 Field55ThirdReimbursementInstitution::B(_) => Some("B"),
292 Field55ThirdReimbursementInstitution::D(_) => Some("D"),
293 }
294 }
295}
296
297#[cfg(test)]
298mod tests {
299 use super::*;
300
301 #[test]
302 fn test_field55a_valid() {
303 let field = Field55A::parse("BNPAFRPPXXX").unwrap();
305 assert_eq!(field.bic, "BNPAFRPPXXX");
306 assert_eq!(field.party_identifier, None);
307 assert_eq!(field.to_swift_string(), ":55A:BNPAFRPPXXX");
308
309 let field = Field55A::parse("/E/55566677\nBNPAFRPP").unwrap();
311 assert_eq!(field.bic, "BNPAFRPP");
312 assert_eq!(field.party_identifier, Some("/E/55566677".to_string()));
313 assert_eq!(field.to_swift_string(), ":55A:/E/55566677\nBNPAFRPP");
314 }
315
316 #[test]
317 fn test_field55b_valid() {
318 let field = Field55B::parse("PARIS BRANCH").unwrap();
320 assert_eq!(field.location, Some("PARIS BRANCH".to_string()));
321 assert_eq!(field.party_identifier, None);
322
323 let field = Field55B::parse("/F/99887766\nPARIS").unwrap();
325 assert_eq!(field.party_identifier, Some("/F/99887766".to_string()));
326 assert_eq!(field.location, Some("PARIS".to_string()));
327
328 let field = Field55B::parse("").unwrap();
330 assert_eq!(field.party_identifier, None);
331 assert_eq!(field.location, None);
332 }
333
334 #[test]
335 fn test_field55d_valid() {
336 let field =
338 Field55D::parse("/E/55566677\nTHIRD BANK\n789 THIRD ST\nPARIS\nFRANCE").unwrap();
339 assert_eq!(field.party_identifier, Some("/E/55566677".to_string()));
340 assert_eq!(field.name_and_address.len(), 4);
341 assert_eq!(field.name_and_address[0], "THIRD BANK");
342 assert_eq!(field.name_and_address[3], "FRANCE");
343
344 let field = Field55D::parse("THIRD BANK\nPARIS").unwrap();
346 assert_eq!(field.party_identifier, None);
347 assert_eq!(field.name_and_address.len(), 2);
348 }
349
350 #[test]
351 fn test_field55_enum() {
352 let field = Field55ThirdReimbursementInstitution::parse("BNPAFRPPXXX").unwrap();
354 assert!(matches!(field, Field55ThirdReimbursementInstitution::A(_)));
355
356 let field = Field55ThirdReimbursementInstitution::parse("PARIS BRANCH").unwrap();
358 assert!(matches!(field, Field55ThirdReimbursementInstitution::B(_)));
359
360 let field =
362 Field55ThirdReimbursementInstitution::parse("BANK NAME\nADDRESS LINE 1\nCITY").unwrap();
363 assert!(matches!(field, Field55ThirdReimbursementInstitution::D(_)));
364 }
365}