use super::field_utils::{parse_name_and_address, parse_party_identifier};
use super::swift_utils::{parse_bic, parse_swift_chars};
use crate::errors::ParseError;
use crate::traits::SwiftField;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub struct Field56A {
pub party_identifier: Option<String>,
pub bic: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub struct Field56C {
pub party_identifier: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub struct Field56D {
pub party_identifier: Option<String>,
pub name_and_address: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub enum Field56Intermediary {
#[serde(rename = "56A")]
A(Field56A),
#[serde(rename = "56C")]
C(Field56C),
#[serde(rename = "56D")]
D(Field56D),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub enum Field56IntermediaryAD {
#[serde(rename = "56A")]
A(Field56A),
#[serde(rename = "56D")]
D(Field56D),
}
impl SwiftField for Field56A {
fn parse(input: &str) -> crate::Result<Self>
where
Self: Sized,
{
let lines: Vec<&str> = input.lines().collect();
if lines.is_empty() {
return Err(ParseError::InvalidFormat {
message: "Field 56A cannot be empty".to_string(),
});
}
let mut party_identifier = None;
let mut bic_line_idx = 0;
if let Some(party_id) = parse_party_identifier(lines[0])? {
party_identifier = Some(party_id);
bic_line_idx = 1;
}
if bic_line_idx >= lines.len() {
return Err(ParseError::InvalidFormat {
message: "Field 56A missing BIC code".to_string(),
});
}
let bic = parse_bic(lines[bic_line_idx])?;
Ok(Field56A {
party_identifier,
bic,
})
}
fn to_swift_string(&self) -> String {
let mut result = Vec::new();
if let Some(ref id) = self.party_identifier {
result.push(format!("/{}", id));
}
result.push(self.bic.clone());
format!(":56A:{}", result.join("\n"))
}
}
impl SwiftField for Field56C {
fn parse(input: &str) -> crate::Result<Self>
where
Self: Sized,
{
if !input.starts_with('/') {
return Err(ParseError::InvalidFormat {
message: "Field 56C must start with '/'".to_string(),
});
}
let identifier = &input[1..];
if identifier.is_empty() || identifier.len() > 34 {
return Err(ParseError::InvalidFormat {
message: "Field 56C party identifier must be 1-34 characters".to_string(),
});
}
parse_swift_chars(identifier, "Field 56C party identifier")?;
Ok(Field56C {
party_identifier: identifier.to_string(),
})
}
fn to_swift_string(&self) -> String {
format!(":56C:/{}", self.party_identifier)
}
}
impl SwiftField for Field56D {
fn parse(input: &str) -> crate::Result<Self>
where
Self: Sized,
{
let lines: Vec<&str> = input.lines().collect();
if lines.is_empty() {
return Err(ParseError::InvalidFormat {
message: "Field 56D must have at least one line".to_string(),
});
}
let mut party_identifier = None;
let mut start_idx = 0;
if let Some(party_id) = parse_party_identifier(lines[0])? {
party_identifier = Some(party_id);
start_idx = 1;
}
let name_and_address = parse_name_and_address(&lines, start_idx, "Field56D")?;
Ok(Field56D {
party_identifier,
name_and_address,
})
}
fn to_swift_string(&self) -> String {
let mut result = Vec::new();
if let Some(ref id) = self.party_identifier {
result.push(format!("/{}", id));
}
for line in &self.name_and_address {
result.push(line.clone());
}
format!(":56D:{}", result.join("\n"))
}
}
impl SwiftField for Field56Intermediary {
fn parse(input: &str) -> crate::Result<Self>
where
Self: Sized,
{
if let Ok(field) = Field56A::parse(input) {
return Ok(Field56Intermediary::A(field));
}
if input.starts_with('/')
&& !input.contains('\n')
&& let Ok(field) = Field56C::parse(input)
{
return Ok(Field56Intermediary::C(field));
}
if let Ok(field) = Field56D::parse(input) {
return Ok(Field56Intermediary::D(field));
}
Err(ParseError::InvalidFormat {
message: "Field 56 Intermediary could not be parsed as option A, C or D".to_string(),
})
}
fn parse_with_variant(
value: &str,
variant: Option<&str>,
_field_tag: Option<&str>,
) -> crate::Result<Self>
where
Self: Sized,
{
match variant {
Some("A") => {
let field = Field56A::parse(value)?;
Ok(Field56Intermediary::A(field))
}
Some("C") => {
let field = Field56C::parse(value)?;
Ok(Field56Intermediary::C(field))
}
Some("D") => {
let field = Field56D::parse(value)?;
Ok(Field56Intermediary::D(field))
}
_ => {
Self::parse(value)
}
}
}
fn to_swift_string(&self) -> String {
match self {
Field56Intermediary::A(field) => field.to_swift_string(),
Field56Intermediary::C(field) => field.to_swift_string(),
Field56Intermediary::D(field) => field.to_swift_string(),
}
}
fn get_variant_tag(&self) -> Option<&'static str> {
match self {
Field56Intermediary::A(_) => Some("A"),
Field56Intermediary::C(_) => Some("C"),
Field56Intermediary::D(_) => Some("D"),
}
}
}
impl SwiftField for Field56IntermediaryAD {
fn parse(input: &str) -> crate::Result<Self>
where
Self: Sized,
{
if let Ok(field) = Field56A::parse(input) {
return Ok(Field56IntermediaryAD::A(field));
}
if let Ok(field) = Field56D::parse(input) {
return Ok(Field56IntermediaryAD::D(field));
}
Err(ParseError::InvalidFormat {
message: "Field 56 Intermediary AD could not be parsed as option A or D".to_string(),
})
}
fn parse_with_variant(
value: &str,
variant: Option<&str>,
_field_tag: Option<&str>,
) -> crate::Result<Self>
where
Self: Sized,
{
match variant {
Some("A") => {
let field = Field56A::parse(value)?;
Ok(Field56IntermediaryAD::A(field))
}
Some("D") => {
let field = Field56D::parse(value)?;
Ok(Field56IntermediaryAD::D(field))
}
_ => {
Self::parse(value)
}
}
}
fn to_swift_string(&self) -> String {
match self {
Field56IntermediaryAD::A(field) => field.to_swift_string(),
Field56IntermediaryAD::D(field) => field.to_swift_string(),
}
}
}
pub type Field56 = Field56Intermediary;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_field56a() {
let field = Field56A::parse("/C/US123456\nDEUTDEFF").unwrap();
assert_eq!(field.party_identifier, Some("C/US123456".to_string()));
assert_eq!(field.bic, "DEUTDEFF");
let field = Field56A::parse("//FW021000018\nCHASUS33XXX").unwrap();
assert_eq!(field.party_identifier, Some("/FW021000018".to_string()));
assert_eq!(field.bic, "CHASUS33XXX");
let field = Field56A::parse("DEUTDEFFXXX").unwrap();
assert_eq!(field.party_identifier, None);
assert_eq!(field.bic, "DEUTDEFFXXX");
}
#[test]
fn test_field56c() {
let field = Field56C::parse("/USCLEARING123").unwrap();
assert_eq!(field.party_identifier, "USCLEARING123");
assert_eq!(field.to_swift_string(), ":56C:/USCLEARING123");
}
#[test]
fn test_field56d() {
let field = Field56D::parse("/D/DE123456\nDEUTSCHE BANK\nFRANKFURT").unwrap();
assert_eq!(field.party_identifier, Some("D/DE123456".to_string()));
assert_eq!(field.name_and_address.len(), 2);
assert_eq!(field.name_and_address[0], "DEUTSCHE BANK");
let field = Field56D::parse("ACME BANK\nNEW YORK").unwrap();
assert_eq!(field.party_identifier, None);
assert_eq!(field.name_and_address.len(), 2);
}
#[test]
fn test_field56_invalid() {
assert!(Field56A::parse("INVALID").is_err());
assert!(Field56C::parse("NOSLASH").is_err());
assert!(Field56D::parse("LINE1\nLINE2\nLINE3\nLINE4\nLINE5").is_err());
}
}