swift_mt_message/fields/
field50.rs

1//! # Field 50: Ordering Customer
2//!
3//! Identifies the payment originator. Different variants provide structured
4//! (BIC/account-based) or flexible (name/address-based) identification.
5//!
6//! **Variants:**
7//! - **A:** Account + numbered name/address lines (STP-optimized)
8//! - **F:** Account + party ID + name/address + BIC (enhanced identification)
9//! - **K:** Account + name/address (flexible format, most common)
10//! - **C:** BIC only (institution-based)
11//! - **G:** Account + BIC
12//! - **H:** Account + name/address
13//! - **L:** Party identifier only
14//! - **No Option:** Name/address only
15//!
16//! **Example:**
17//! ```text
18//! :50K:/DE89370400440532013000
19//! JOHN DOE
20//! 123 MAIN STREET
21//! ```
22
23use super::swift_utils::{parse_bic, parse_swift_chars};
24use crate::errors::ParseError;
25use crate::traits::SwiftField;
26use serde::{Deserialize, Serialize};
27
28/// **Field 50 (No Option): Name/Address Only**
29///
30/// Free-format name and address (4*35x).
31#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
32pub struct Field50NoOption {
33    /// Name/address (max 4 lines, 35 chars each)
34    pub name_and_address: Vec<String>,
35}
36
37impl SwiftField for Field50NoOption {
38    fn parse(input: &str) -> crate::Result<Self>
39    where
40        Self: Sized,
41    {
42        let lines: Vec<String> = input.lines().map(|line| line.to_string()).collect();
43
44        if lines.is_empty() {
45            return Err(ParseError::InvalidFormat {
46                message: "Field 50 (No Option) must have at least one line".to_string(),
47            });
48        }
49
50        if lines.len() > 4 {
51            return Err(ParseError::InvalidFormat {
52                message: format!(
53                    "Field 50 (No Option) cannot have more than 4 lines, found {}",
54                    lines.len()
55                ),
56            });
57        }
58
59        // Validate each line
60        for (i, line) in lines.iter().enumerate() {
61            if line.len() > 35 {
62                return Err(ParseError::InvalidFormat {
63                    message: format!("Field 50 (No Option) line {} exceeds 35 characters", i + 1),
64                });
65            }
66            parse_swift_chars(line, &format!("Field 50 (No Option) line {}", i + 1))?;
67        }
68
69        Ok(Field50NoOption {
70            name_and_address: lines,
71        })
72    }
73
74    fn to_swift_string(&self) -> String {
75        format!(":50:{}", self.name_and_address.join("\n"))
76    }
77}
78
79/// **Field 50A: Account + Numbered Name/Address**
80///
81/// Structured format with numbered lines (1/text, 2/text, etc.).
82/// Format: [/34x]4*(1!n/33x)
83#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
84pub struct Field50A {
85    /// Optional account/party ID (max 34 chars)
86    pub party_identifier: Option<String>,
87    /// Numbered name/address lines (e.g., "1/ACME CORP")
88    pub name_and_address: Vec<String>,
89}
90
91impl SwiftField for Field50A {
92    fn parse(input: &str) -> crate::Result<Self>
93    where
94        Self: Sized,
95    {
96        let lines: Vec<&str> = input.lines().collect();
97
98        if lines.is_empty() {
99            return Err(ParseError::InvalidFormat {
100                message: "Field 50A must have at least one line".to_string(),
101            });
102        }
103
104        let mut party_identifier = None;
105        let mut name_and_address = Vec::new();
106        let mut start_index = 0;
107
108        // Check if first line is party identifier
109        if lines[0].starts_with('/') {
110            let identifier = &lines[0][1..];
111            if identifier.len() > 34 {
112                return Err(ParseError::InvalidFormat {
113                    message: "Field 50A party identifier exceeds 34 characters".to_string(),
114                });
115            }
116            parse_swift_chars(identifier, "Field 50A party identifier")?;
117            party_identifier = Some(identifier.to_string());
118            start_index = 1;
119        }
120
121        // Parse numbered name/address lines
122        for (i, line) in lines.iter().enumerate().skip(start_index) {
123            // Expected format: digit/text (e.g., "1/ACME CORP")
124            if line.len() < 2 || !line.chars().next().is_some_and(|c| c.is_ascii_digit()) {
125                return Err(ParseError::InvalidFormat {
126                    message: format!(
127                        "Field 50A line {} must start with line number",
128                        i - start_index + 1
129                    ),
130                });
131            }
132
133            if line.chars().nth(1) != Some('/') {
134                return Err(ParseError::InvalidFormat {
135                    message: format!(
136                        "Field 50A line {} must have '/' after line number",
137                        i - start_index + 1
138                    ),
139                });
140            }
141
142            let text = &line[2..];
143            if text.len() > 33 {
144                return Err(ParseError::InvalidFormat {
145                    message: format!(
146                        "Field 50A line {} text exceeds 33 characters",
147                        i - start_index + 1
148                    ),
149                });
150            }
151
152            parse_swift_chars(text, &format!("Field 50A line {}", i - start_index + 1))?;
153            name_and_address.push(text.to_string());
154        }
155
156        if name_and_address.is_empty() {
157            return Err(ParseError::InvalidFormat {
158                message: "Field 50A must have at least one name/address line".to_string(),
159            });
160        }
161
162        if name_and_address.len() > 4 {
163            return Err(ParseError::InvalidFormat {
164                message: format!(
165                    "Field 50A cannot have more than 4 name/address lines, found {}",
166                    name_and_address.len()
167                ),
168            });
169        }
170
171        Ok(Field50A {
172            party_identifier,
173            name_and_address,
174        })
175    }
176
177    fn to_swift_string(&self) -> String {
178        let mut result = Vec::new();
179
180        if let Some(ref id) = self.party_identifier {
181            result.push(format!("/{}", id));
182        }
183
184        for (i, line) in self.name_and_address.iter().enumerate() {
185            result.push(format!("{}/{}", i + 1, line));
186        }
187
188        format!(":50A:{}", result.join("\n"))
189    }
190}
191
192/// **Field 50F: Account + Party ID + Name/Address + BIC**
193///
194/// Enhanced identification with BIC.
195/// Format: account + [/party_id] + [name/address] + BIC
196#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
197pub struct Field50F {
198    /// Account (max 35 chars)
199    pub account: String,
200    /// Optional party ID (max 34 chars)
201    #[serde(skip_serializing_if = "Option::is_none")]
202    pub party_identifier: Option<String>,
203    /// Optional name/address (1-4 lines)
204    #[serde(skip_serializing_if = "Option::is_none")]
205    pub name_and_address: Option<Vec<String>>,
206    /// BIC code
207    pub bic: String,
208}
209
210impl SwiftField for Field50F {
211    fn parse(input: &str) -> crate::Result<Self>
212    where
213        Self: Sized,
214    {
215        let lines: Vec<&str> = input.lines().collect();
216
217        if lines.len() < 2 {
218            return Err(ParseError::InvalidFormat {
219                message: format!(
220                    "Field 50F must have at least 2 lines (account + BIC), found {}",
221                    lines.len()
222                ),
223            });
224        }
225
226        // Parse account (first line)
227        let account = lines[0];
228        if account.is_empty() || account.len() > 35 {
229            return Err(ParseError::InvalidFormat {
230                message: "Field 50F account must be 1-35 characters".to_string(),
231            });
232        }
233        parse_swift_chars(account, "Field 50F account")?;
234
235        // Find BIC line (last line)
236        let bic = parse_bic(lines[lines.len() - 1])?;
237
238        // Check if there's a party identifier (line starting with /) after account
239        let mut party_identifier = None;
240        let mut name_start = 1;
241
242        if lines.len() > 2 && lines[1].starts_with('/') {
243            let party_id = &lines[1][1..]; // Remove leading slash
244            if party_id.len() > 34 {
245                return Err(ParseError::InvalidFormat {
246                    message: "Field 50F party identifier exceeds 34 characters".to_string(),
247                });
248            }
249            parse_swift_chars(party_id, "Field 50F party identifier")?;
250            party_identifier = Some(party_id.to_string());
251            name_start = 2;
252        }
253
254        // Parse name and address lines (between party_id/account and BIC)
255        let mut name_and_address = Vec::new();
256        for line in &lines[name_start..lines.len() - 1] {
257            if line.len() > 35 {
258                return Err(ParseError::InvalidFormat {
259                    message: "Field 50F name/address line exceeds 35 characters".to_string(),
260                });
261            }
262            parse_swift_chars(line, "Field 50F name/address")?;
263            name_and_address.push(line.to_string());
264        }
265
266        Ok(Field50F {
267            account: account.to_string(),
268            party_identifier,
269            name_and_address: if name_and_address.is_empty() {
270                None
271            } else {
272                Some(name_and_address)
273            },
274            bic,
275        })
276    }
277
278    fn to_swift_string(&self) -> String {
279        let mut lines = vec![self.account.clone()];
280
281        if let Some(ref party_id) = self.party_identifier {
282            lines.push(format!("/{}", party_id));
283        }
284
285        if let Some(ref addr) = self.name_and_address {
286            lines.extend(addr.clone());
287        }
288
289        lines.push(self.bic.clone());
290
291        format!(":50F:{}", lines.join("\n"))
292    }
293}
294
295/// **Field 50K: Account + Free-Format Name/Address**
296///
297/// Most common variant. Free-format name and address.
298/// Format: [/34x]4*35x
299#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
300pub struct Field50K {
301    /// Optional account (max 34 chars)
302    pub account: Option<String>,
303    /// Name/address (max 4 lines, 35 chars each)
304    pub name_and_address: Vec<String>,
305}
306
307impl SwiftField for Field50K {
308    fn parse(input: &str) -> crate::Result<Self>
309    where
310        Self: Sized,
311    {
312        let lines: Vec<&str> = input.lines().collect();
313
314        if lines.is_empty() {
315            return Err(ParseError::InvalidFormat {
316                message: "Field 50K must have at least one line".to_string(),
317            });
318        }
319
320        let mut account = None;
321        let mut name_and_address = Vec::new();
322        let mut start_index = 0;
323
324        // Check if first line is account (with leading slash in MT format)
325        if lines[0].starts_with('/') {
326            let acc = &lines[0][1..];
327            if acc.len() > 34 {
328                return Err(ParseError::InvalidFormat {
329                    message: "Field 50K account exceeds 34 characters".to_string(),
330                });
331            }
332            parse_swift_chars(acc, "Field 50K account")?;
333            // Store account without the slash
334            account = Some(acc.to_string());
335            start_index = 1;
336        }
337
338        // Parse name/address lines
339        for (i, line) in lines.iter().enumerate().skip(start_index) {
340            if line.len() > 35 {
341                return Err(ParseError::InvalidFormat {
342                    message: format!(
343                        "Field 50K line {} exceeds 35 characters",
344                        i - start_index + 1
345                    ),
346                });
347            }
348            parse_swift_chars(line, &format!("Field 50K line {}", i - start_index + 1))?;
349            name_and_address.push(line.to_string());
350        }
351
352        if name_and_address.is_empty() {
353            return Err(ParseError::InvalidFormat {
354                message: "Field 50K must have at least one name/address line".to_string(),
355            });
356        }
357
358        if name_and_address.len() > 4 {
359            return Err(ParseError::InvalidFormat {
360                message: format!(
361                    "Field 50K cannot have more than 4 name/address lines, found {}",
362                    name_and_address.len()
363                ),
364            });
365        }
366
367        Ok(Field50K {
368            account,
369            name_and_address,
370        })
371    }
372
373    fn to_swift_string(&self) -> String {
374        let mut result = Vec::new();
375
376        // Add slash prefix when converting to MT format
377        if let Some(ref acc) = self.account {
378            result.push(format!("/{}", acc));
379        }
380
381        for line in &self.name_and_address {
382            result.push(line.clone());
383        }
384
385        format!(":50K:{}", result.join("\n"))
386    }
387}
388
389/// **Field 50C: BIC Only**
390///
391/// Institution-based identification.
392#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
393pub struct Field50C {
394    /// BIC code (8 or 11 chars)
395    pub bic: String,
396}
397
398impl SwiftField for Field50C {
399    fn parse(input: &str) -> crate::Result<Self>
400    where
401        Self: Sized,
402    {
403        let bic = parse_bic(input)?;
404        Ok(Field50C { bic })
405    }
406
407    fn to_swift_string(&self) -> String {
408        format!(":50C:{}", self.bic)
409    }
410}
411
412/// **Field 50L: Party Identifier Only**
413///
414/// Single-line party identifier (35x).
415#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
416pub struct Field50L {
417    /// Party ID (max 35 chars)
418    pub party_identifier: String,
419}
420
421impl SwiftField for Field50L {
422    fn parse(input: &str) -> crate::Result<Self>
423    where
424        Self: Sized,
425    {
426        // Field 50L should be a single-line party identifier
427        // Reject if contains newlines (which would indicate it's a different variant)
428        if input.contains('\n') {
429            return Err(ParseError::InvalidFormat {
430                message: "Field 50L party identifier must be single line".to_string(),
431            });
432        }
433
434        if input.is_empty() || input.len() > 35 {
435            return Err(ParseError::InvalidFormat {
436                message: "Field 50L party identifier must be 1-35 characters".to_string(),
437            });
438        }
439
440        parse_swift_chars(input, "Field 50L party identifier")?;
441
442        Ok(Field50L {
443            party_identifier: input.to_string(),
444        })
445    }
446
447    fn to_swift_string(&self) -> String {
448        format!(":50L:{}", self.party_identifier)
449    }
450}
451
452/// **Field 50G: Account + BIC**
453///
454/// Simple account and BIC identification.
455#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
456pub struct Field50G {
457    /// Account (max 34 chars)
458    pub account: String,
459    /// BIC code
460    pub bic: String,
461}
462
463impl SwiftField for Field50G {
464    fn parse(input: &str) -> crate::Result<Self>
465    where
466        Self: Sized,
467    {
468        let lines: Vec<&str> = input.lines().collect();
469
470        if lines.len() != 2 {
471            return Err(ParseError::InvalidFormat {
472                message: format!("Field 50G must have exactly 2 lines, found {}", lines.len()),
473            });
474        }
475
476        // Parse account (first line, must start with /)
477        if !lines[0].starts_with('/') {
478            return Err(ParseError::InvalidFormat {
479                message: "Field 50G account must start with '/'".to_string(),
480            });
481        }
482
483        let account = &lines[0][1..];
484        if account.is_empty() || account.len() > 34 {
485            return Err(ParseError::InvalidFormat {
486                message: "Field 50G account must be 1-34 characters".to_string(),
487            });
488        }
489        parse_swift_chars(account, "Field 50G account")?;
490
491        // Parse BIC (second line)
492        let bic = parse_bic(lines[1])?;
493
494        Ok(Field50G {
495            account: account.to_string(),
496            bic,
497        })
498    }
499
500    fn to_swift_string(&self) -> String {
501        format!(":50G:/{}\n{}", self.account, self.bic)
502    }
503}
504
505/// **Field 50H: Account + Name/Address**
506///
507/// Account with free-format name/address.
508#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
509pub struct Field50H {
510    /// Account (max 34 chars)
511    pub account: String,
512    /// Name/address (max 4 lines, 35 chars each)
513    pub name_and_address: Vec<String>,
514}
515
516impl SwiftField for Field50H {
517    fn parse(input: &str) -> crate::Result<Self>
518    where
519        Self: Sized,
520    {
521        let lines: Vec<&str> = input.lines().collect();
522
523        if lines.len() < 2 {
524            return Err(ParseError::InvalidFormat {
525                message: "Field 50H must have at least 2 lines".to_string(),
526            });
527        }
528
529        // Parse account (first line, must start with /)
530        if !lines[0].starts_with('/') {
531            return Err(ParseError::InvalidFormat {
532                message: "Field 50H account must start with '/'".to_string(),
533            });
534        }
535
536        let account = &lines[0][1..];
537        if account.is_empty() || account.len() > 34 {
538            return Err(ParseError::InvalidFormat {
539                message: "Field 50H account must be 1-34 characters".to_string(),
540            });
541        }
542        parse_swift_chars(account, "Field 50H account")?;
543
544        // Parse name/address lines
545        let mut name_and_address = Vec::new();
546        for (i, line) in lines.iter().enumerate().skip(1) {
547            if line.len() > 35 {
548                return Err(ParseError::InvalidFormat {
549                    message: format!("Field 50H line {} exceeds 35 characters", i),
550                });
551            }
552            parse_swift_chars(line, &format!("Field 50H line {}", i))?;
553            name_and_address.push(line.to_string());
554        }
555
556        if name_and_address.len() > 4 {
557            return Err(ParseError::InvalidFormat {
558                message: format!(
559                    "Field 50H cannot have more than 4 name/address lines, found {}",
560                    name_and_address.len()
561                ),
562            });
563        }
564
565        Ok(Field50H {
566            account: account.to_string(),
567            name_and_address,
568        })
569    }
570
571    fn to_swift_string(&self) -> String {
572        let mut result = vec![format!("/{}", self.account)];
573        result.extend(self.name_and_address.clone());
574        format!(":50H:{}", result.join("\n"))
575    }
576}
577
578/// Enum for Field50 Instructing Party variants (C, L)
579#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
580pub enum Field50InstructingParty {
581    #[serde(rename = "50C")]
582    C(Field50C),
583    #[serde(rename = "50L")]
584    L(Field50L),
585}
586
587impl SwiftField for Field50InstructingParty {
588    fn parse(input: &str) -> crate::Result<Self>
589    where
590        Self: Sized,
591    {
592        // Try to detect variant by format
593        // Option C is a BIC (8 or 11 characters)
594        // Option L is a party identifier (up to 35 characters)
595
596        let trimmed = input.trim();
597
598        // Try parsing as BIC first (more restrictive)
599        if let Ok(field) = Field50C::parse(trimmed) {
600            return Ok(Field50InstructingParty::C(field));
601        }
602
603        // Try parsing as party identifier
604        if let Ok(field) = Field50L::parse(trimmed) {
605            return Ok(Field50InstructingParty::L(field));
606        }
607
608        Err(ParseError::InvalidFormat {
609            message: "Field 50 Instructing Party could not be parsed as option C or L".to_string(),
610        })
611    }
612
613    fn parse_with_variant(
614        value: &str,
615        variant: Option<&str>,
616        _field_tag: Option<&str>,
617    ) -> crate::Result<Self>
618    where
619        Self: Sized,
620    {
621        match variant {
622            Some("C") => {
623                let field = Field50C::parse(value)?;
624                Ok(Field50InstructingParty::C(field))
625            }
626            Some("L") => {
627                let field = Field50L::parse(value)?;
628                Ok(Field50InstructingParty::L(field))
629            }
630            _ => {
631                // No variant specified, fall back to default parse behavior
632                Self::parse(value)
633            }
634        }
635    }
636
637    fn to_swift_string(&self) -> String {
638        match self {
639            Field50InstructingParty::C(field) => field.to_swift_string(),
640            Field50InstructingParty::L(field) => field.to_swift_string(),
641        }
642    }
643}
644
645/// Enum for Field50 Ordering Customer variants (F, G, H)
646#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
647pub enum Field50OrderingCustomerFGH {
648    #[serde(rename = "50F")]
649    F(Field50F),
650    #[serde(rename = "50G")]
651    G(Field50G),
652    #[serde(rename = "50H")]
653    H(Field50H),
654}
655
656impl SwiftField for Field50OrderingCustomerFGH {
657    fn parse(input: &str) -> crate::Result<Self>
658    where
659        Self: Sized,
660    {
661        let lines: Vec<&str> = input.lines().collect();
662
663        if lines.len() >= 2 {
664            // Check if second line is a BIC
665            if (8..=11).contains(&lines[1].len()) {
666                // Could be F or G
667                if lines[0].starts_with('/') {
668                    // Option G: /account + BIC
669                    if let Ok(field) = Field50G::parse(input) {
670                        return Ok(Field50OrderingCustomerFGH::G(field));
671                    }
672                } else {
673                    // Option F: account + BIC
674                    if let Ok(field) = Field50F::parse(input) {
675                        return Ok(Field50OrderingCustomerFGH::F(field));
676                    }
677                }
678            }
679
680            // Try Option H: /account + name/address
681            if lines[0].starts_with('/')
682                && let Ok(field) = Field50H::parse(input)
683            {
684                return Ok(Field50OrderingCustomerFGH::H(field));
685            }
686        }
687
688        Err(ParseError::InvalidFormat {
689            message: "Field 50 Ordering Customer could not be parsed as option F, G or H"
690                .to_string(),
691        })
692    }
693
694    fn parse_with_variant(
695        value: &str,
696        variant: Option<&str>,
697        _field_tag: Option<&str>,
698    ) -> crate::Result<Self>
699    where
700        Self: Sized,
701    {
702        match variant {
703            Some("F") => {
704                let field = Field50F::parse(value)?;
705                Ok(Field50OrderingCustomerFGH::F(field))
706            }
707            Some("G") => {
708                let field = Field50G::parse(value)?;
709                Ok(Field50OrderingCustomerFGH::G(field))
710            }
711            Some("H") => {
712                let field = Field50H::parse(value)?;
713                Ok(Field50OrderingCustomerFGH::H(field))
714            }
715            _ => {
716                // No variant specified, fall back to default parse behavior
717                Self::parse(value)
718            }
719        }
720    }
721
722    fn to_swift_string(&self) -> String {
723        match self {
724            Field50OrderingCustomerFGH::F(field) => field.to_swift_string(),
725            Field50OrderingCustomerFGH::G(field) => field.to_swift_string(),
726            Field50OrderingCustomerFGH::H(field) => field.to_swift_string(),
727        }
728    }
729}
730
731/// Enum for Field50 Ordering Customer variants (A, F, K)
732#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
733pub enum Field50OrderingCustomerAFK {
734    #[serde(rename = "50A")]
735    A(Field50A),
736    #[serde(rename = "50F")]
737    F(Field50F),
738    #[serde(rename = "50K")]
739    K(Field50K),
740}
741
742impl SwiftField for Field50OrderingCustomerAFK {
743    fn parse(input: &str) -> crate::Result<Self>
744    where
745        Self: Sized,
746    {
747        // Try Option A first (numbered lines)
748        let lines: Vec<&str> = input.lines().collect();
749
750        // Check for numbered lines (characteristic of Option A)
751        let mut has_numbered_lines = false;
752        for line in &lines {
753            let mut chars = line.chars();
754            if line.len() >= 2
755                && chars.next().is_some_and(|c| c.is_ascii_digit())
756                && chars.next() == Some('/')
757            {
758                has_numbered_lines = true;
759                break;
760            }
761        }
762
763        if has_numbered_lines && let Ok(field) = Field50A::parse(input) {
764            return Ok(Field50OrderingCustomerAFK::A(field));
765        }
766
767        // Try Option F (account + BIC)
768        if lines.len() == 2
769            && (8..=11).contains(&lines[1].len())
770            && let Ok(field) = Field50F::parse(input)
771        {
772            return Ok(Field50OrderingCustomerAFK::F(field));
773        }
774
775        // Try Option K (flexible format)
776        if let Ok(field) = Field50K::parse(input) {
777            return Ok(Field50OrderingCustomerAFK::K(field));
778        }
779
780        Err(ParseError::InvalidFormat {
781            message: "Field 50 Ordering Customer could not be parsed as option A, F or K"
782                .to_string(),
783        })
784    }
785
786    fn parse_with_variant(
787        value: &str,
788        variant: Option<&str>,
789        _field_tag: Option<&str>,
790    ) -> crate::Result<Self>
791    where
792        Self: Sized,
793    {
794        match variant {
795            Some("A") => {
796                let field = Field50A::parse(value)?;
797                Ok(Field50OrderingCustomerAFK::A(field))
798            }
799            Some("F") => {
800                let field = Field50F::parse(value)?;
801                Ok(Field50OrderingCustomerAFK::F(field))
802            }
803            Some("K") => {
804                let field = Field50K::parse(value)?;
805                Ok(Field50OrderingCustomerAFK::K(field))
806            }
807            _ => {
808                // No variant specified, fall back to default parse behavior
809                Self::parse(value)
810            }
811        }
812    }
813
814    fn to_swift_string(&self) -> String {
815        match self {
816            Field50OrderingCustomerAFK::A(field) => field.to_swift_string(),
817            Field50OrderingCustomerAFK::F(field) => field.to_swift_string(),
818            Field50OrderingCustomerAFK::K(field) => field.to_swift_string(),
819        }
820    }
821
822    fn get_variant_tag(&self) -> Option<&'static str> {
823        match self {
824            Field50OrderingCustomerAFK::A(_) => Some("A"),
825            Field50OrderingCustomerAFK::F(_) => Some("F"),
826            Field50OrderingCustomerAFK::K(_) => Some("K"),
827        }
828    }
829}
830
831/// Enum for Field50 Ordering Customer variants (No Option, C, F)
832#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
833pub enum Field50OrderingCustomerNCF {
834    #[serde(rename = "50")]
835    NoOption(Field50NoOption),
836    #[serde(rename = "50C")]
837    C(Field50C),
838    #[serde(rename = "50F")]
839    F(Field50F),
840}
841
842impl SwiftField for Field50OrderingCustomerNCF {
843    fn parse(input: &str) -> crate::Result<Self>
844    where
845        Self: Sized,
846    {
847        let lines: Vec<&str> = input.lines().collect();
848
849        // Try Option C (single line BIC)
850        if lines.len() == 1
851            && (8..=11).contains(&lines[0].len())
852            && let Ok(field) = Field50C::parse(input)
853        {
854            return Ok(Field50OrderingCustomerNCF::C(field));
855        }
856
857        // Try Option F (account + BIC)
858        if lines.len() == 2
859            && (8..=11).contains(&lines[1].len())
860            && let Ok(field) = Field50F::parse(input)
861        {
862            return Ok(Field50OrderingCustomerNCF::F(field));
863        }
864
865        // Try No Option (name/address only)
866        if let Ok(field) = Field50NoOption::parse(input) {
867            return Ok(Field50OrderingCustomerNCF::NoOption(field));
868        }
869
870        Err(ParseError::InvalidFormat {
871            message: "Field 50 Ordering Customer could not be parsed as No Option, C or F"
872                .to_string(),
873        })
874    }
875
876    fn parse_with_variant(
877        value: &str,
878        variant: Option<&str>,
879        _field_tag: Option<&str>,
880    ) -> crate::Result<Self>
881    where
882        Self: Sized,
883    {
884        match variant {
885            None => {
886                let field = Field50NoOption::parse(value)?;
887                Ok(Field50OrderingCustomerNCF::NoOption(field))
888            }
889            Some("C") => {
890                let field = Field50C::parse(value)?;
891                Ok(Field50OrderingCustomerNCF::C(field))
892            }
893            Some("F") => {
894                let field = Field50F::parse(value)?;
895                Ok(Field50OrderingCustomerNCF::F(field))
896            }
897            _ => {
898                // Unknown variant, fall back to default parse behavior
899                Self::parse(value)
900            }
901        }
902    }
903
904    fn to_swift_string(&self) -> String {
905        match self {
906            Field50OrderingCustomerNCF::NoOption(field) => field.to_swift_string(),
907            Field50OrderingCustomerNCF::C(field) => field.to_swift_string(),
908            Field50OrderingCustomerNCF::F(field) => field.to_swift_string(),
909        }
910    }
911}
912
913/// Enum for Field50 Creditor variants (A, K)
914#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
915pub enum Field50Creditor {
916    #[serde(rename = "50A")]
917    A(Field50A),
918    #[serde(rename = "50K")]
919    K(Field50K),
920}
921
922impl SwiftField for Field50Creditor {
923    fn parse(input: &str) -> crate::Result<Self>
924    where
925        Self: Sized,
926    {
927        // Check for numbered lines (characteristic of Option A)
928        let lines: Vec<&str> = input.lines().collect();
929
930        for line in &lines {
931            let mut chars = line.chars();
932            if line.len() >= 2
933                && chars.next().is_some_and(|c| c.is_ascii_digit())
934                && chars.next() == Some('/')
935            {
936                // Has numbered lines, try Option A
937                if let Ok(field) = Field50A::parse(input) {
938                    return Ok(Field50Creditor::A(field));
939                }
940            }
941        }
942
943        // Try Option K
944        if let Ok(field) = Field50K::parse(input) {
945            return Ok(Field50Creditor::K(field));
946        }
947
948        Err(ParseError::InvalidFormat {
949            message: "Field 50 Creditor could not be parsed as option A or K".to_string(),
950        })
951    }
952
953    fn parse_with_variant(
954        value: &str,
955        variant: Option<&str>,
956        _field_tag: Option<&str>,
957    ) -> crate::Result<Self>
958    where
959        Self: Sized,
960    {
961        match variant {
962            Some("A") => {
963                let field = Field50A::parse(value)?;
964                Ok(Field50Creditor::A(field))
965            }
966            Some("K") => {
967                let field = Field50K::parse(value)?;
968                Ok(Field50Creditor::K(field))
969            }
970            _ => {
971                // No variant specified, fall back to default parse behavior
972                Self::parse(value)
973            }
974        }
975    }
976
977    fn to_swift_string(&self) -> String {
978        match self {
979            Field50Creditor::A(field) => field.to_swift_string(),
980            Field50Creditor::K(field) => field.to_swift_string(),
981        }
982    }
983
984    fn get_variant_tag(&self) -> Option<&'static str> {
985        match self {
986            Field50Creditor::A(_) => Some("A"),
987            Field50Creditor::K(_) => Some("K"),
988        }
989    }
990}
991
992// Type alias for backward compatibility - most common use case is ordering customer
993pub type Field50 = Field50OrderingCustomerNCF;
994
995#[cfg(test)]
996mod tests {
997    use super::*;
998
999    #[test]
1000    fn test_field50_no_option() {
1001        let field = Field50NoOption::parse("JOHN DOE\n123 MAIN ST\nNEW YORK").unwrap();
1002        assert_eq!(field.name_and_address.len(), 3);
1003        assert_eq!(field.name_and_address[0], "JOHN DOE");
1004        assert_eq!(
1005            field.to_swift_string(),
1006            ":50:JOHN DOE\n123 MAIN ST\nNEW YORK"
1007        );
1008    }
1009
1010    #[test]
1011    fn test_field50a() {
1012        let field =
1013            Field50A::parse("/US123456789\n1/ACME CORP\n2/123 MAIN ST\n3/NEW YORK").unwrap();
1014        assert_eq!(field.party_identifier, Some("US123456789".to_string()));
1015        assert_eq!(field.name_and_address.len(), 3);
1016        assert_eq!(field.name_and_address[0], "ACME CORP");
1017
1018        let swift_str = field.to_swift_string();
1019        assert!(swift_str.starts_with(":50A:"));
1020        assert!(swift_str.contains("/US123456789"));
1021        assert!(swift_str.contains("1/ACME CORP"));
1022    }
1023
1024    #[test]
1025    fn test_field50f() {
1026        let field = Field50F::parse("ACCOUNT123\nDEUTDEFFXXX").unwrap();
1027        assert_eq!(field.account, "ACCOUNT123");
1028        assert_eq!(field.bic, "DEUTDEFFXXX");
1029        assert_eq!(field.to_swift_string(), ":50F:ACCOUNT123\nDEUTDEFFXXX");
1030    }
1031
1032    #[test]
1033    fn test_field50k() {
1034        let field = Field50K::parse(
1035            "/DE89370400440532013000\nJOHN DOE\n123 MAIN STREET\nNEW YORK NY 10001",
1036        )
1037        .unwrap();
1038        assert_eq!(field.account, Some("DE89370400440532013000".to_string()));
1039        assert_eq!(field.name_and_address[0], "JOHN DOE");
1040        assert_eq!(field.name_and_address.len(), 3);
1041    }
1042
1043    #[test]
1044    fn test_field50c() {
1045        let field = Field50C::parse("DEUTDEFF").unwrap();
1046        assert_eq!(field.bic, "DEUTDEFF");
1047        assert_eq!(field.to_swift_string(), ":50C:DEUTDEFF");
1048    }
1049
1050    #[test]
1051    fn test_field50l() {
1052        let field = Field50L::parse("PARTY123").unwrap();
1053        assert_eq!(field.party_identifier, "PARTY123");
1054        assert_eq!(field.to_swift_string(), ":50L:PARTY123");
1055    }
1056
1057    #[test]
1058    fn test_field50g() {
1059        let field = Field50G::parse("/ACCOUNT456\nCHASUS33XXX").unwrap();
1060        assert_eq!(field.account, "ACCOUNT456");
1061        assert_eq!(field.bic, "CHASUS33XXX");
1062        assert_eq!(field.to_swift_string(), ":50G:/ACCOUNT456\nCHASUS33XXX");
1063    }
1064
1065    #[test]
1066    fn test_field50h() {
1067        let field = Field50H::parse("/ACCOUNT789\nJANE SMITH\n456 ELM ST").unwrap();
1068        assert_eq!(field.account, "ACCOUNT789");
1069        assert_eq!(field.name_and_address.len(), 2);
1070        assert_eq!(field.name_and_address[0], "JANE SMITH");
1071    }
1072
1073    #[test]
1074    fn test_field50_ordering_customer_afk() {
1075        // Test Option A
1076        let field = Field50OrderingCustomerAFK::parse("1/ACME CORP\n2/NEW YORK").unwrap();
1077        assert!(matches!(field, Field50OrderingCustomerAFK::A(_)));
1078
1079        // Test Option K
1080        let field = Field50OrderingCustomerAFK::parse("/ACC123\nJOHN DOE").unwrap();
1081        assert!(matches!(field, Field50OrderingCustomerAFK::K(_)));
1082
1083        // Test Option F
1084        let field = Field50OrderingCustomerAFK::parse("ACCOUNT\nDEUTDEFF").unwrap();
1085        assert!(matches!(field, Field50OrderingCustomerAFK::F(_)));
1086    }
1087}