1use super::field_utils::{parse_name_and_address, parse_party_identifier};
2use super::swift_utils::{parse_bic, parse_swift_chars};
3use crate::errors::ParseError;
4use crate::traits::SwiftField;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
20pub struct Field56A {
21 pub party_identifier: Option<String>,
23
24 pub bic: String,
26}
27
28#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
33pub struct Field56C {
34 pub party_identifier: String,
36}
37
38#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
43pub struct Field56D {
44 pub party_identifier: Option<String>,
46
47 pub name_and_address: Vec<String>,
49}
50
51#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
52pub enum Field56Intermediary {
53 #[serde(rename = "56A")]
54 A(Field56A),
55 #[serde(rename = "56C")]
56 C(Field56C),
57 #[serde(rename = "56D")]
58 D(Field56D),
59}
60
61#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
62pub enum Field56IntermediaryAD {
63 #[serde(rename = "56A")]
64 A(Field56A),
65 #[serde(rename = "56D")]
66 D(Field56D),
67}
68
69impl SwiftField for Field56A {
70 fn parse(input: &str) -> crate::Result<Self>
71 where
72 Self: Sized,
73 {
74 let lines: Vec<&str> = input.lines().collect();
75
76 if lines.is_empty() {
77 return Err(ParseError::InvalidFormat {
78 message: "Field 56A cannot be empty".to_string(),
79 });
80 }
81
82 let mut party_identifier = None;
83 let mut bic_line_idx = 0;
84
85 if let Some(party_id) = parse_party_identifier(lines[0])? {
87 party_identifier = Some(party_id);
88 bic_line_idx = 1;
89 }
90
91 if bic_line_idx >= lines.len() {
93 return Err(ParseError::InvalidFormat {
94 message: "Field 56A missing BIC code".to_string(),
95 });
96 }
97
98 let bic = parse_bic(lines[bic_line_idx])?;
99
100 Ok(Field56A {
101 party_identifier,
102 bic,
103 })
104 }
105
106 fn to_swift_string(&self) -> String {
107 let mut result = Vec::new();
108
109 if let Some(ref id) = self.party_identifier {
110 result.push(format!("/{}", id));
111 }
112
113 result.push(self.bic.clone());
114 format!(":56A:{}", result.join("\n"))
115 }
116}
117
118impl SwiftField for Field56C {
119 fn parse(input: &str) -> crate::Result<Self>
120 where
121 Self: Sized,
122 {
123 if !input.starts_with('/') {
124 return Err(ParseError::InvalidFormat {
125 message: "Field 56C must start with '/'".to_string(),
126 });
127 }
128
129 let identifier = &input[1..];
130
131 if identifier.is_empty() || identifier.len() > 34 {
132 return Err(ParseError::InvalidFormat {
133 message: "Field 56C party identifier must be 1-34 characters".to_string(),
134 });
135 }
136
137 parse_swift_chars(identifier, "Field 56C party identifier")?;
138
139 Ok(Field56C {
140 party_identifier: identifier.to_string(),
141 })
142 }
143
144 fn to_swift_string(&self) -> String {
145 format!(":56C:/{}", self.party_identifier)
146 }
147}
148
149impl SwiftField for Field56D {
150 fn parse(input: &str) -> crate::Result<Self>
151 where
152 Self: Sized,
153 {
154 let lines: Vec<&str> = input.lines().collect();
155
156 if lines.is_empty() {
157 return Err(ParseError::InvalidFormat {
158 message: "Field 56D must have at least one line".to_string(),
159 });
160 }
161
162 let mut party_identifier = None;
163 let mut start_idx = 0;
164
165 if let Some(party_id) = parse_party_identifier(lines[0])? {
167 party_identifier = Some(party_id);
168 start_idx = 1;
169 }
170
171 let name_and_address = parse_name_and_address(&lines, start_idx, "Field56D")?;
173
174 Ok(Field56D {
175 party_identifier,
176 name_and_address,
177 })
178 }
179
180 fn to_swift_string(&self) -> String {
181 let mut result = Vec::new();
182
183 if let Some(ref id) = self.party_identifier {
184 result.push(format!("/{}", id));
185 }
186
187 for line in &self.name_and_address {
188 result.push(line.clone());
189 }
190
191 format!(":56D:{}", result.join("\n"))
192 }
193}
194
195impl SwiftField for Field56Intermediary {
196 fn parse(input: &str) -> crate::Result<Self>
197 where
198 Self: Sized,
199 {
200 if let Ok(field) = Field56A::parse(input) {
202 return Ok(Field56Intermediary::A(field));
203 }
204
205 if input.starts_with('/')
207 && !input.contains('\n')
208 && let Ok(field) = Field56C::parse(input)
209 {
210 return Ok(Field56Intermediary::C(field));
211 }
212
213 if let Ok(field) = Field56D::parse(input) {
215 return Ok(Field56Intermediary::D(field));
216 }
217
218 Err(ParseError::InvalidFormat {
219 message: "Field 56 Intermediary could not be parsed as option A, C or D".to_string(),
220 })
221 }
222
223 fn parse_with_variant(
224 value: &str,
225 variant: Option<&str>,
226 _field_tag: Option<&str>,
227 ) -> crate::Result<Self>
228 where
229 Self: Sized,
230 {
231 match variant {
232 Some("A") => {
233 let field = Field56A::parse(value)?;
234 Ok(Field56Intermediary::A(field))
235 }
236 Some("C") => {
237 let field = Field56C::parse(value)?;
238 Ok(Field56Intermediary::C(field))
239 }
240 Some("D") => {
241 let field = Field56D::parse(value)?;
242 Ok(Field56Intermediary::D(field))
243 }
244 _ => {
245 Self::parse(value)
247 }
248 }
249 }
250
251 fn to_swift_string(&self) -> String {
252 match self {
253 Field56Intermediary::A(field) => field.to_swift_string(),
254 Field56Intermediary::C(field) => field.to_swift_string(),
255 Field56Intermediary::D(field) => field.to_swift_string(),
256 }
257 }
258
259 fn get_variant_tag(&self) -> Option<&'static str> {
260 match self {
261 Field56Intermediary::A(_) => Some("A"),
262 Field56Intermediary::C(_) => Some("C"),
263 Field56Intermediary::D(_) => Some("D"),
264 }
265 }
266}
267
268impl SwiftField for Field56IntermediaryAD {
269 fn parse(input: &str) -> crate::Result<Self>
270 where
271 Self: Sized,
272 {
273 if let Ok(field) = Field56A::parse(input) {
275 return Ok(Field56IntermediaryAD::A(field));
276 }
277
278 if let Ok(field) = Field56D::parse(input) {
280 return Ok(Field56IntermediaryAD::D(field));
281 }
282
283 Err(ParseError::InvalidFormat {
284 message: "Field 56 Intermediary AD could not be parsed as option A or D".to_string(),
285 })
286 }
287
288 fn parse_with_variant(
289 value: &str,
290 variant: Option<&str>,
291 _field_tag: Option<&str>,
292 ) -> crate::Result<Self>
293 where
294 Self: Sized,
295 {
296 match variant {
297 Some("A") => {
298 let field = Field56A::parse(value)?;
299 Ok(Field56IntermediaryAD::A(field))
300 }
301 Some("D") => {
302 let field = Field56D::parse(value)?;
303 Ok(Field56IntermediaryAD::D(field))
304 }
305 _ => {
306 Self::parse(value)
308 }
309 }
310 }
311
312 fn to_swift_string(&self) -> String {
313 match self {
314 Field56IntermediaryAD::A(field) => field.to_swift_string(),
315 Field56IntermediaryAD::D(field) => field.to_swift_string(),
316 }
317 }
318}
319
320pub type Field56 = Field56Intermediary;
322
323#[cfg(test)]
324mod tests {
325 use super::*;
326
327 #[test]
328 fn test_field56a() {
329 let field = Field56A::parse("/C/US123456\nDEUTDEFF").unwrap();
331 assert_eq!(field.party_identifier, Some("C/US123456".to_string()));
332 assert_eq!(field.bic, "DEUTDEFF");
333
334 let field = Field56A::parse("//FW021000018\nCHASUS33XXX").unwrap();
336 assert_eq!(field.party_identifier, Some("/FW021000018".to_string()));
337 assert_eq!(field.bic, "CHASUS33XXX");
338
339 let field = Field56A::parse("DEUTDEFFXXX").unwrap();
341 assert_eq!(field.party_identifier, None);
342 assert_eq!(field.bic, "DEUTDEFFXXX");
343 }
344
345 #[test]
346 fn test_field56c() {
347 let field = Field56C::parse("/USCLEARING123").unwrap();
348 assert_eq!(field.party_identifier, "USCLEARING123");
349 assert_eq!(field.to_swift_string(), ":56C:/USCLEARING123");
350 }
351
352 #[test]
353 fn test_field56d() {
354 let field = Field56D::parse("/D/DE123456\nDEUTSCHE BANK\nFRANKFURT").unwrap();
356 assert_eq!(field.party_identifier, Some("D/DE123456".to_string()));
357 assert_eq!(field.name_and_address.len(), 2);
358 assert_eq!(field.name_and_address[0], "DEUTSCHE BANK");
359
360 let field = Field56D::parse("ACME BANK\nNEW YORK").unwrap();
362 assert_eq!(field.party_identifier, None);
363 assert_eq!(field.name_and_address.len(), 2);
364 }
365
366 #[test]
367 fn test_field56_invalid() {
368 assert!(Field56A::parse("INVALID").is_err());
370
371 assert!(Field56C::parse("NOSLASH").is_err());
373
374 assert!(Field56D::parse("LINE1\nLINE2\nLINE3\nLINE4\nLINE5").is_err());
376 }
377}