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)]
12pub struct Field59F {
13 pub party_identifier: Option<String>,
15 pub name_and_address: Vec<String>,
17}
18
19#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
24pub struct Field59A {
25 pub account: Option<String>,
27 pub bic: String,
29}
30
31#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
36pub struct Field59NoOption {
37 pub account: Option<String>,
39 pub name_and_address: Vec<String>,
41}
42
43#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
59pub enum Field59 {
60 #[serde(rename = "59A")]
61 A(Field59A),
62 #[serde(rename = "59F")]
63 F(Field59F),
64 #[serde(rename = "59")]
65 NoOption(Field59NoOption),
66}
67
68impl SwiftField for Field59F {
69 fn parse(input: &str) -> crate::Result<Self>
70 where
71 Self: Sized,
72 {
73 let lines: Vec<&str> = input.lines().collect();
74
75 if lines.is_empty() {
76 return Err(ParseError::InvalidFormat {
77 message: "Field 59F cannot be empty".to_string(),
78 });
79 }
80
81 let mut party_identifier = None;
82 let mut start_idx = 0;
83
84 if let Some(party_id) = parse_party_identifier(lines[0])? {
86 party_identifier = Some(party_id);
87 start_idx = 1;
88 }
89
90 let mut name_and_address = Vec::new();
92 for (i, line) in lines.iter().enumerate().skip(start_idx) {
93 let mut chars = line.chars();
95 let first_char = chars.next();
96 let second_char = chars.next();
97 if line.len() < 2 || !first_char.unwrap().is_ascii_digit() || second_char != Some('/') {
98 return Err(ParseError::InvalidFormat {
99 message: format!(
100 "Field 59F line {} must start with line number and slash (e.g., '1/')",
101 i - start_idx + 1
102 ),
103 });
104 }
105
106 let line_num = first_char.unwrap().to_digit(10).unwrap() as usize;
107 let expected_line_num = i - start_idx + 1;
108
109 if line_num != expected_line_num {
110 return Err(ParseError::InvalidFormat {
111 message: format!(
112 "Field 59F line number {} doesn't match expected {}",
113 line_num, expected_line_num
114 ),
115 });
116 }
117
118 let content = &line[2..];
119 if content.len() > 33 {
120 return Err(ParseError::InvalidFormat {
121 message: format!("Field 59F line {} content exceeds 33 characters", line_num),
122 });
123 }
124
125 parse_swift_chars(content, &format!("Field 59F line {}", line_num))?;
126 name_and_address.push(line.to_string());
127 }
128
129 if name_and_address.is_empty() {
130 return Err(ParseError::InvalidFormat {
131 message: "Field 59F must have at least one name/address line".to_string(),
132 });
133 }
134
135 if name_and_address.len() > 4 {
136 return Err(ParseError::InvalidFormat {
137 message: format!(
138 "Field 59F cannot have more than 4 name/address lines, found {}",
139 name_and_address.len()
140 ),
141 });
142 }
143
144 Ok(Field59F {
145 party_identifier,
146 name_and_address,
147 })
148 }
149
150 fn to_swift_string(&self) -> String {
151 let mut result = String::from(":59F:");
152
153 if let Some(ref id) = self.party_identifier {
154 result.push_str(&format!("/{}\n", id));
155 }
156
157 for (i, line) in self.name_and_address.iter().enumerate() {
158 if i > 0 || self.party_identifier.is_some() {
159 result.push('\n');
160 }
161 result.push_str(line);
162 }
163
164 result
165 }
166}
167
168impl SwiftField for Field59A {
169 fn parse(input: &str) -> crate::Result<Self>
170 where
171 Self: Sized,
172 {
173 let lines: Vec<&str> = input.lines().collect();
174
175 if lines.is_empty() {
176 return Err(ParseError::InvalidFormat {
177 message: "Field 59A cannot be empty".to_string(),
178 });
179 }
180
181 let mut account = None;
182 let bic_line_idx;
183
184 if lines[0].starts_with('/') {
186 let identifier = &lines[0][1..];
187 if identifier.len() <= 34 {
188 parse_swift_chars(identifier, "Field 59A account")?;
189 account = Some(identifier.to_string());
190 bic_line_idx = 1;
191 } else {
192 bic_line_idx = 0;
193 }
194 } else {
195 bic_line_idx = 0;
196 }
197
198 if bic_line_idx >= lines.len() {
200 return Err(ParseError::InvalidFormat {
201 message: "Field 59A missing BIC code".to_string(),
202 });
203 }
204
205 let bic = parse_bic(lines[bic_line_idx])?;
206
207 Ok(Field59A { account, bic })
208 }
209
210 fn to_swift_string(&self) -> String {
211 let mut result = String::from(":59A:");
212
213 if let Some(ref acc) = self.account {
214 result.push_str(&format!("/{}\n", acc));
215 }
216
217 result.push_str(&self.bic);
218 result
219 }
220}
221
222impl SwiftField for Field59NoOption {
223 fn parse(input: &str) -> crate::Result<Self>
224 where
225 Self: Sized,
226 {
227 let lines: Vec<&str> = input.lines().collect();
228
229 if lines.is_empty() {
230 return Err(ParseError::InvalidFormat {
231 message: "Field 59 (No Option) cannot be empty".to_string(),
232 });
233 }
234
235 let mut account = None;
236 let mut start_idx = 0;
237
238 if lines[0].starts_with('/') {
240 let identifier = &lines[0][1..];
241 if identifier.len() <= 34 {
242 parse_swift_chars(identifier, "Field 59 account")?;
243 account = Some(identifier.to_string());
244 start_idx = 1;
245 }
246 }
247
248 let name_and_address = parse_name_and_address(&lines, start_idx, "Field59NoOption")?;
250
251 Ok(Field59NoOption {
252 account,
253 name_and_address,
254 })
255 }
256
257 fn to_swift_string(&self) -> String {
258 let mut result = String::from(":59:");
259
260 if let Some(ref acc) = self.account {
261 result.push_str(&format!("/{}", acc));
262 }
263
264 for (i, line) in self.name_and_address.iter().enumerate() {
265 if i > 0 || (i == 0 && self.account.is_some()) {
266 result.push('\n');
267 }
268 result.push_str(line);
269 }
270
271 result
272 }
273}
274
275impl SwiftField for Field59 {
276 fn parse(input: &str) -> crate::Result<Self>
277 where
278 Self: Sized,
279 {
280 if let Ok(field) = Field59A::parse(input) {
282 return Ok(Field59::A(field));
283 }
284
285 let lines: Vec<&str> = input.lines().collect();
288 if !lines.is_empty() {
289 let check_start = if lines[0].starts_with('/') { 1 } else { 0 };
291 if check_start < lines.len() {
292 let test_line = lines[check_start];
293 let mut chars = test_line.chars();
294 if test_line.len() >= 2
295 && chars.next().unwrap().is_ascii_digit()
296 && chars.next() == Some('/')
297 && let Ok(field) = Field59F::parse(input)
298 {
299 return Ok(Field59::F(field));
300 }
301 }
302 }
303
304 if let Ok(field) = Field59NoOption::parse(input) {
306 return Ok(Field59::NoOption(field));
307 }
308
309 Err(ParseError::InvalidFormat {
310 message: "Field 59 could not be parsed as option A, F, or No Option".to_string(),
311 })
312 }
313
314 fn parse_with_variant(
315 value: &str,
316 variant: Option<&str>,
317 _field_tag: Option<&str>,
318 ) -> crate::Result<Self>
319 where
320 Self: Sized,
321 {
322 match variant {
323 None => {
324 let field = Field59NoOption::parse(value)?;
325 Ok(Field59::NoOption(field))
326 }
327 Some("A") => {
328 let field = Field59A::parse(value)?;
329 Ok(Field59::A(field))
330 }
331 Some("F") => {
332 let field = Field59F::parse(value)?;
333 Ok(Field59::F(field))
334 }
335 _ => {
336 Self::parse(value)
338 }
339 }
340 }
341
342 fn to_swift_string(&self) -> String {
343 match self {
344 Field59::A(field) => field.to_swift_string(),
345 Field59::F(field) => field.to_swift_string(),
346 Field59::NoOption(field) => field.to_swift_string(),
347 }
348 }
349
350 fn get_variant_tag(&self) -> Option<&'static str> {
351 match self {
352 Field59::A(_) => Some("A"),
353 Field59::F(_) => Some("F"),
354 Field59::NoOption(_) => None, }
356 }
357}
358
359impl SwiftField for Field59Debtor {
360 fn parse(input: &str) -> crate::Result<Self>
361 where
362 Self: Sized,
363 {
364 if let Ok(field) = Field59A::parse(input) {
366 return Ok(Field59Debtor::A(field));
367 }
368
369 if let Ok(field) = Field59NoOption::parse(input) {
371 return Ok(Field59Debtor::NoOption(field));
372 }
373
374 Err(ParseError::InvalidFormat {
375 message: "Field 59 Debtor could not be parsed as option A or No Option".to_string(),
376 })
377 }
378
379 fn parse_with_variant(
380 value: &str,
381 variant: Option<&str>,
382 _field_tag: Option<&str>,
383 ) -> crate::Result<Self>
384 where
385 Self: Sized,
386 {
387 match variant {
388 None => {
389 let field = Field59NoOption::parse(value)?;
390 Ok(Field59Debtor::NoOption(field))
391 }
392 Some("A") => {
393 let field = Field59A::parse(value)?;
394 Ok(Field59Debtor::A(field))
395 }
396 _ => {
397 Self::parse(value)
399 }
400 }
401 }
402
403 fn to_swift_string(&self) -> String {
404 match self {
405 Field59Debtor::A(field) => field.to_swift_string(),
406 Field59Debtor::NoOption(field) => field.to_swift_string(),
407 }
408 }
409}
410
411#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
412pub enum Field59Debtor {
413 #[serde(rename = "59A")]
414 A(Field59A),
415 #[serde(rename = "59")]
416 NoOption(Field59NoOption),
417}
418
419#[cfg(test)]
420mod tests {
421 use super::*;
422
423 #[test]
424 fn test_field59f() {
425 let field = Field59F::parse("/GB82WEST12345698765432\n1/ACME CORPORATION LIMITED\n2/INTERNATIONAL TRADE DIVISION\n3/123 BUSINESS PARK AVENUE\n4/LONDON EC1A 1BB UNITED KINGDOM").unwrap();
427 assert_eq!(
428 field.party_identifier,
429 Some("GB82WEST12345698765432".to_string())
430 );
431 assert_eq!(field.name_and_address.len(), 4);
432 assert_eq!(field.name_and_address[0], "1/ACME CORPORATION LIMITED");
433
434 let field =
436 Field59F::parse("1/JOHN SMITH\n2/123 MAIN STREET\n3/LONDON\n4/UNITED KINGDOM").unwrap();
437 assert_eq!(field.party_identifier, None);
438 assert_eq!(field.name_and_address.len(), 4);
439 }
440
441 #[test]
442 fn test_field59a() {
443 let field = Field59A::parse("/GB82WEST12345698765432\nMIDLGB22XXX").unwrap();
445 assert_eq!(field.account, Some("GB82WEST12345698765432".to_string()));
446 assert_eq!(field.bic, "MIDLGB22XXX");
447
448 let field = Field59A::parse("CHASUS33XXX").unwrap();
450 assert_eq!(field.account, None);
451 assert_eq!(field.bic, "CHASUS33XXX");
452 }
453
454 #[test]
455 fn test_field59_no_option() {
456 let field = Field59NoOption::parse("/GB82WEST12345698765432\nJOHN SMITH\n456 RESIDENTIAL AVENUE\nMANCHESTER M1 1AA\nUNITED KINGDOM").unwrap();
458 assert_eq!(field.account, Some("GB82WEST12345698765432".to_string()));
459 assert_eq!(field.name_and_address.len(), 4);
460 assert_eq!(field.name_and_address[0], "JOHN SMITH");
461
462 let field = Field59NoOption::parse("JANE DOE\n789 MAIN STREET\nLONDON").unwrap();
464 assert_eq!(field.account, None);
465 assert_eq!(field.name_and_address.len(), 3);
466 }
467
468 #[test]
469 fn test_field59_invalid() {
470 assert!(Field59A::parse("INVALID").is_err());
472
473 assert!(Field59F::parse("2/WRONG LINE NUMBER").is_err());
475
476 assert!(Field59NoOption::parse("LINE1\nLINE2\nLINE3\nLINE4\nLINE5").is_err());
478 }
479}