1use crate::common::BIC;
2use crate::{SwiftField, ValidationError, ValidationResult};
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
112pub struct Field53A {
113 #[serde(skip_serializing_if = "Option::is_none")]
115 pub account_line_indicator: Option<String>,
116 #[serde(skip_serializing_if = "Option::is_none")]
118 pub account_number: Option<String>,
119 #[serde(flatten)]
121 pub bic: BIC,
122}
123
124impl Field53A {
125 pub fn new(
127 account_line_indicator: Option<String>,
128 account_number: Option<String>,
129 bic: impl Into<String>,
130 ) -> Result<Self, crate::ParseError> {
131 let bic = bic.into().to_uppercase();
132
133 if let Some(ref indicator) = account_line_indicator {
135 if indicator.is_empty() {
136 return Err(crate::ParseError::InvalidFieldFormat {
137 field_tag: "53A".to_string(),
138 message: "Account line indicator cannot be empty if specified".to_string(),
139 });
140 }
141
142 if indicator.len() != 1 {
143 return Err(crate::ParseError::InvalidFieldFormat {
144 field_tag: "53A".to_string(),
145 message: "Account line indicator must be exactly 1 character".to_string(),
146 });
147 }
148
149 if !indicator.chars().all(|c| c.is_ascii() && !c.is_control()) {
150 return Err(crate::ParseError::InvalidFieldFormat {
151 field_tag: "53A".to_string(),
152 message: "Account line indicator contains invalid characters".to_string(),
153 });
154 }
155 }
156
157 if let Some(ref account) = account_number {
159 if account.is_empty() {
160 return Err(crate::ParseError::InvalidFieldFormat {
161 field_tag: "53A".to_string(),
162 message: "Account number cannot be empty if specified".to_string(),
163 });
164 }
165
166 if account.len() > 34 {
167 return Err(crate::ParseError::InvalidFieldFormat {
168 field_tag: "53A".to_string(),
169 message: "Account number too long (max 34 characters)".to_string(),
170 });
171 }
172
173 if !account.chars().all(|c| c.is_ascii() && !c.is_control()) {
174 return Err(crate::ParseError::InvalidFieldFormat {
175 field_tag: "53A".to_string(),
176 message: "Account number contains invalid characters".to_string(),
177 });
178 }
179 }
180
181 let parsed_bic = BIC::parse(&bic, Some("53A"))?;
183
184 Ok(Field53A {
185 account_line_indicator,
186 account_number,
187 bic: parsed_bic,
188 })
189 }
190
191 pub fn account_line_indicator(&self) -> Option<&str> {
193 self.account_line_indicator.as_deref()
194 }
195
196 pub fn account_number(&self) -> Option<&str> {
198 self.account_number.as_deref()
199 }
200
201 pub fn bic(&self) -> &str {
203 self.bic.value()
204 }
205
206 pub fn is_full_bic(&self) -> bool {
208 self.bic.is_full_bic()
209 }
210
211 pub fn bank_code(&self) -> &str {
213 self.bic.bank_code()
214 }
215
216 pub fn country_code(&self) -> &str {
218 self.bic.country_code()
219 }
220
221 pub fn location_code(&self) -> &str {
223 self.bic.location_code()
224 }
225
226 pub fn branch_code(&self) -> Option<&str> {
228 self.bic.branch_code()
229 }
230
231 pub fn description(&self) -> String {
233 match &self.account_number {
234 Some(account) => format!("Sender's Correspondent: {} ({})", self.bic, account),
235 None => format!("Sender's Correspondent: {}", self.bic),
236 }
237 }
238}
239
240impl SwiftField for Field53A {
241 fn parse(value: &str) -> Result<Self, crate::ParseError> {
242 let content = if let Some(stripped) = value.strip_prefix(":53A:") {
243 stripped } else if let Some(stripped) = value.strip_prefix("53A:") {
245 stripped } else {
247 value
248 };
249
250 let content = content.trim();
251
252 if content.is_empty() {
253 return Err(crate::ParseError::InvalidFieldFormat {
254 field_tag: "53A".to_string(),
255 message: "Field content cannot be empty".to_string(),
256 });
257 }
258
259 let mut account_number = None;
261 let bic;
262
263 if content.starts_with('/') {
264 let lines: Vec<&str> = content.lines().collect();
266
267 if lines.len() == 1 {
268 let parts: Vec<&str> = lines[0].splitn(2, ' ').collect();
270 if parts.len() == 2 {
271 account_number = Some(parts[0][1..].to_string()); bic = parts[1].to_string();
273 } else {
274 return Err(crate::ParseError::InvalidFieldFormat {
275 field_tag: "53A".to_string(),
276 message: "Invalid format: expected account and BIC".to_string(),
277 });
278 }
279 } else if lines.len() == 2 {
280 account_number = Some(lines[0][1..].to_string()); bic = lines[1].to_string();
283 } else {
284 return Err(crate::ParseError::InvalidFieldFormat {
285 field_tag: "53A".to_string(),
286 message: "Invalid format: too many lines".to_string(),
287 });
288 }
289 } else {
290 bic = content.to_string();
292 }
293
294 let parsed_bic = BIC::parse(&bic, Some("53A"))?;
295
296 Ok(Field53A {
297 account_line_indicator: None,
298 account_number,
299 bic: parsed_bic,
300 })
301 }
302
303 fn to_swift_string(&self) -> String {
304 match &self.account_number {
305 Some(account) => format!(":53A:/{}\n{}", account, self.bic.value()),
306 None => format!(":53A:{}", self.bic.value()),
307 }
308 }
309
310 fn validate(&self) -> ValidationResult {
311 let mut errors = Vec::new();
312
313 if let Some(ref indicator) = self.account_line_indicator {
315 if indicator.is_empty() {
316 errors.push(ValidationError::ValueValidation {
317 field_tag: "53A".to_string(),
318 message: "Account line indicator cannot be empty if specified".to_string(),
319 });
320 }
321
322 if indicator.len() != 1 {
323 errors.push(ValidationError::LengthValidation {
324 field_tag: "53A".to_string(),
325 expected: "exactly 1 character".to_string(),
326 actual: indicator.len(),
327 });
328 }
329
330 if !indicator.chars().all(|c| c.is_ascii() && !c.is_control()) {
331 errors.push(ValidationError::FormatValidation {
332 field_tag: "53A".to_string(),
333 message: "Account line indicator contains invalid characters".to_string(),
334 });
335 }
336 }
337
338 if let Some(ref account) = self.account_number {
340 if account.is_empty() {
341 errors.push(ValidationError::ValueValidation {
342 field_tag: "53A".to_string(),
343 message: "Account number cannot be empty if specified".to_string(),
344 });
345 }
346
347 if account.len() > 34 {
348 errors.push(ValidationError::LengthValidation {
349 field_tag: "53A".to_string(),
350 expected: "max 34 characters".to_string(),
351 actual: account.len(),
352 });
353 }
354
355 if !account.chars().all(|c| c.is_ascii() && !c.is_control()) {
356 errors.push(ValidationError::FormatValidation {
357 field_tag: "53A".to_string(),
358 message: "Account number contains invalid characters".to_string(),
359 });
360 }
361 }
362
363 let bic_validation = self.bic.validate();
365 if !bic_validation.is_valid {
366 errors.extend(bic_validation.errors);
367 }
368
369 ValidationResult {
370 is_valid: errors.is_empty(),
371 errors,
372 warnings: Vec::new(),
373 }
374 }
375
376 fn format_spec() -> &'static str {
377 "[/1!c][/34x]BIC"
378 }
379}
380
381impl std::fmt::Display for Field53A {
382 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
383 match (&self.account_line_indicator, &self.account_number) {
384 (Some(indicator), Some(account)) => write!(f, "/{}{} {}", indicator, account, self.bic),
385 (None, Some(account)) => write!(f, "/{} {}", account, self.bic),
386 _ => write!(f, "{}", self.bic),
387 }
388 }
389}
390
391#[cfg(test)]
392mod tests {
393 use super::*;
394
395 #[test]
396 fn test_field53a_creation_bic_only() {
397 let field = Field53A::new(None, None, "DEUTDEFF").unwrap();
398 assert_eq!(field.bic(), "DEUTDEFF");
399 assert!(field.account_number().is_none());
400 assert!(!field.is_full_bic());
401 }
402
403 #[test]
404 fn test_field53a_creation_with_account() {
405 let field = Field53A::new(None, Some("1234567890".to_string()), "DEUTDEFF500").unwrap();
406 assert_eq!(field.bic(), "DEUTDEFF500");
407 assert_eq!(field.account_number(), Some("1234567890"));
408 assert!(field.is_full_bic());
409 }
410
411 #[test]
412 fn test_field53a_parse_bic_only() {
413 let field = Field53A::parse("CHASUS33").unwrap();
414 assert_eq!(field.bic(), "CHASUS33");
415 assert!(field.account_number().is_none());
416 }
417
418 #[test]
419 fn test_field53a_parse_with_account_same_line() {
420 let field = Field53A::parse("/1234567890 DEUTDEFF").unwrap();
421 assert_eq!(field.bic(), "DEUTDEFF");
422 assert_eq!(field.account_number(), Some("1234567890"));
423 }
424
425 #[test]
426 fn test_field53a_parse_with_account_separate_lines() {
427 let field = Field53A::parse("/1234567890\nDEUTDEFF500").unwrap();
428 assert_eq!(field.bic(), "DEUTDEFF500");
429 assert_eq!(field.account_number(), Some("1234567890"));
430 }
431
432 #[test]
433 fn test_field53a_parse_with_prefix() {
434 let field = Field53A::parse(":53A:CHASUS33").unwrap();
435 assert_eq!(field.bic(), "CHASUS33");
436 }
437
438 #[test]
439 fn test_field53a_to_swift_string_bic_only() {
440 let field = Field53A::new(None, None, "DEUTDEFF").unwrap();
441 assert_eq!(field.to_swift_string(), ":53A:DEUTDEFF");
442 }
443
444 #[test]
445 fn test_field53a_to_swift_string_with_account() {
446 let field = Field53A::new(None, Some("1234567890".to_string()), "DEUTDEFF500").unwrap();
447 assert_eq!(field.to_swift_string(), ":53A:/1234567890\nDEUTDEFF500");
448 }
449
450 #[test]
451 fn test_field53a_bic_components() {
452 let field = Field53A::new(None, None, "DEUTDEFF500").unwrap();
453 assert_eq!(field.bank_code(), "DEUT");
454 assert_eq!(field.country_code(), "DE");
455 assert_eq!(field.location_code(), "FF");
456 assert_eq!(field.branch_code(), Some("500"));
457 }
458
459 #[test]
460 fn test_field53a_short_bic_components() {
461 let field = Field53A::new(None, None, "CHASUS33").unwrap();
462 assert_eq!(field.bank_code(), "CHAS");
463 assert_eq!(field.country_code(), "US");
464 assert_eq!(field.location_code(), "33");
465 assert_eq!(field.branch_code(), None);
466 }
467
468 #[test]
469 fn test_field53a_invalid_bic_length() {
470 let result = Field53A::new(None, None, "DEUT");
471 assert!(result.is_err());
472
473 let result = Field53A::new(None, None, "DEUTDEFF5001");
474 assert!(result.is_err());
475 }
476
477 #[test]
478 fn test_field53a_invalid_bic_format() {
479 let result = Field53A::new(None, None, "123TDEFF");
480 assert!(result.is_err());
481
482 let result = Field53A::new(None, None, "DEUT12FF");
483 assert!(result.is_err());
484
485 let result = Field53A::new(None, None, "DEUTDE@F");
486 assert!(result.is_err());
487 }
488
489 #[test]
490 fn test_field53a_invalid_account() {
491 let result = Field53A::new(None, Some("".to_string()), "DEUTDEFF");
492 assert!(result.is_err());
493
494 let result = Field53A::new(None, Some("A".repeat(35)), "DEUTDEFF");
495 assert!(result.is_err());
496 }
497
498 #[test]
499 fn test_field53a_validation() {
500 let field = Field53A::new(None, Some("1234567890".to_string()), "DEUTDEFF").unwrap();
501 let validation = field.validate();
502 assert!(validation.is_valid);
503 assert!(validation.errors.is_empty());
504 }
505
506 #[test]
507 fn test_field53a_display() {
508 let field1 = Field53A::new(None, None, "DEUTDEFF").unwrap();
509 assert_eq!(format!("{}", field1), "DEUTDEFF");
510
511 let field2 = Field53A::new(None, Some("1234567890".to_string()), "DEUTDEFF").unwrap();
512 assert_eq!(format!("{}", field2), "/1234567890 DEUTDEFF");
513 }
514
515 #[test]
516 fn test_field53a_description() {
517 let field1 = Field53A::new(None, None, "DEUTDEFF").unwrap();
518 assert_eq!(field1.description(), "Sender's Correspondent: DEUTDEFF");
519
520 let field2 = Field53A::new(None, Some("1234567890".to_string()), "DEUTDEFF").unwrap();
521 assert_eq!(
522 field2.description(),
523 "Sender's Correspondent: DEUTDEFF (1234567890)"
524 );
525 }
526
527 #[test]
528 fn test_field53a_case_normalization() {
529 let field = Field53A::new(None, None, "deutdeff").unwrap();
530 assert_eq!(field.bic(), "DEUTDEFF");
531 }
532}