use super::field_utils::{parse_name_and_address, parse_party_identifier};
use super::swift_utils::{parse_bic, parse_max_length};
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 Field55A {
pub party_identifier: Option<String>,
pub bic: String,
}
impl SwiftField for Field55A {
fn parse(input: &str) -> crate::Result<Self>
where
Self: Sized,
{
let lines: Vec<&str> = input.split('\n').collect();
if lines.is_empty() {
return Err(ParseError::InvalidFormat {
message: "Field 55A requires input".to_string(),
});
}
let mut line_idx = 0;
let mut party_identifier = None;
if let Some(party_id) = parse_party_identifier(lines[0])? {
party_identifier = Some(format!("/{}", party_id));
line_idx = 1;
}
if line_idx >= lines.len() {
return Err(ParseError::InvalidFormat {
message: "Field 55A requires BIC code after party identifier".to_string(),
});
}
let bic = parse_bic(lines[line_idx])?;
Ok(Field55A {
party_identifier,
bic,
})
}
fn to_swift_string(&self) -> String {
let mut result = String::from(":55A:");
if let Some(ref party_id) = self.party_identifier {
result.push_str(party_id);
result.push('\n');
}
result.push_str(&self.bic);
result
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub struct Field55B {
pub party_identifier: Option<String>,
pub location: Option<String>,
}
impl SwiftField for Field55B {
fn parse(input: &str) -> crate::Result<Self>
where
Self: Sized,
{
if input.is_empty() {
return Ok(Field55B {
party_identifier: None,
location: None,
});
}
let lines: Vec<&str> = input.split('\n').collect();
let mut party_identifier = None;
let mut location = None;
let mut line_idx = 0;
if !lines.is_empty() && lines[0].starts_with('/') {
party_identifier = Some(lines[0].to_string());
line_idx = 1;
}
if line_idx < lines.len() && !lines[line_idx].is_empty() {
location = Some(parse_max_length(lines[line_idx], 35, "Field55B location")?);
}
Ok(Field55B {
party_identifier,
location,
})
}
fn to_swift_string(&self) -> String {
let mut result = String::from(":55B:");
if let Some(ref party_id) = self.party_identifier {
result.push_str(party_id);
if self.location.is_some() {
result.push('\n');
}
}
if let Some(ref loc) = self.location {
result.push_str(loc);
}
result
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub struct Field55D {
pub party_identifier: Option<String>,
pub name_and_address: Vec<String>,
}
impl SwiftField for Field55D {
fn parse(input: &str) -> crate::Result<Self>
where
Self: Sized,
{
let lines: Vec<&str> = input.split('\n').collect();
if lines.is_empty() {
return Err(ParseError::InvalidFormat {
message: "Field 55D requires 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(format!("/{}", party_id));
start_idx = 1;
}
let name_and_address = parse_name_and_address(&lines, start_idx, "Field55D")?;
Ok(Field55D {
party_identifier,
name_and_address,
})
}
fn to_swift_string(&self) -> String {
let mut result = String::from(":55D:");
if let Some(ref party_id) = self.party_identifier {
result.push_str(party_id);
result.push('\n');
}
for (i, line) in self.name_and_address.iter().enumerate() {
if i > 0 {
result.push('\n');
}
result.push_str(line);
}
result
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub enum Field55ThirdReimbursementInstitution {
#[serde(rename = "55A")]
A(Field55A),
#[serde(rename = "55B")]
B(Field55B),
#[serde(rename = "55D")]
D(Field55D),
}
impl SwiftField for Field55ThirdReimbursementInstitution {
fn parse(input: &str) -> crate::Result<Self>
where
Self: Sized,
{
let lines: Vec<&str> = input.split('\n').collect();
let last_line = lines.last().unwrap_or(&"");
if (8..=11).contains(&last_line.len())
&& last_line
.chars()
.all(|c| c.is_ascii_uppercase() || c.is_ascii_digit())
{
if let Ok(field) = Field55A::parse(input) {
return Ok(Field55ThirdReimbursementInstitution::A(field));
}
}
if lines.len() > 2 || (lines.len() == 2 && !lines[0].starts_with('/')) {
if let Ok(field) = Field55D::parse(input) {
return Ok(Field55ThirdReimbursementInstitution::D(field));
}
}
if let Ok(field) = Field55B::parse(input) {
return Ok(Field55ThirdReimbursementInstitution::B(field));
}
if let Ok(field) = Field55A::parse(input) {
return Ok(Field55ThirdReimbursementInstitution::A(field));
}
if let Ok(field) = Field55D::parse(input) {
return Ok(Field55ThirdReimbursementInstitution::D(field));
}
Err(ParseError::InvalidFormat {
message: "Field 55 could not be parsed as any valid option (A, B, 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 = Field55A::parse(value)?;
Ok(Field55ThirdReimbursementInstitution::A(field))
}
Some("B") => {
let field = Field55B::parse(value)?;
Ok(Field55ThirdReimbursementInstitution::B(field))
}
Some("D") => {
let field = Field55D::parse(value)?;
Ok(Field55ThirdReimbursementInstitution::D(field))
}
_ => {
Self::parse(value)
}
}
}
fn to_swift_string(&self) -> String {
match self {
Field55ThirdReimbursementInstitution::A(field) => field.to_swift_string(),
Field55ThirdReimbursementInstitution::B(field) => field.to_swift_string(),
Field55ThirdReimbursementInstitution::D(field) => field.to_swift_string(),
}
}
fn get_variant_tag(&self) -> Option<&'static str> {
match self {
Field55ThirdReimbursementInstitution::A(_) => Some("A"),
Field55ThirdReimbursementInstitution::B(_) => Some("B"),
Field55ThirdReimbursementInstitution::D(_) => Some("D"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_field55a_valid() {
let field = Field55A::parse("BNPAFRPPXXX").unwrap();
assert_eq!(field.bic, "BNPAFRPPXXX");
assert_eq!(field.party_identifier, None);
assert_eq!(field.to_swift_string(), ":55A:BNPAFRPPXXX");
let field = Field55A::parse("/E/55566677\nBNPAFRPP").unwrap();
assert_eq!(field.bic, "BNPAFRPP");
assert_eq!(field.party_identifier, Some("/E/55566677".to_string()));
assert_eq!(field.to_swift_string(), ":55A:/E/55566677\nBNPAFRPP");
}
#[test]
fn test_field55b_valid() {
let field = Field55B::parse("PARIS BRANCH").unwrap();
assert_eq!(field.location, Some("PARIS BRANCH".to_string()));
assert_eq!(field.party_identifier, None);
let field = Field55B::parse("/F/99887766\nPARIS").unwrap();
assert_eq!(field.party_identifier, Some("/F/99887766".to_string()));
assert_eq!(field.location, Some("PARIS".to_string()));
let field = Field55B::parse("").unwrap();
assert_eq!(field.party_identifier, None);
assert_eq!(field.location, None);
}
#[test]
fn test_field55d_valid() {
let field =
Field55D::parse("/E/55566677\nTHIRD BANK\n789 THIRD ST\nPARIS\nFRANCE").unwrap();
assert_eq!(field.party_identifier, Some("/E/55566677".to_string()));
assert_eq!(field.name_and_address.len(), 4);
assert_eq!(field.name_and_address[0], "THIRD BANK");
assert_eq!(field.name_and_address[3], "FRANCE");
let field = Field55D::parse("THIRD BANK\nPARIS").unwrap();
assert_eq!(field.party_identifier, None);
assert_eq!(field.name_and_address.len(), 2);
}
#[test]
fn test_field55_enum() {
let field = Field55ThirdReimbursementInstitution::parse("BNPAFRPPXXX").unwrap();
assert!(matches!(field, Field55ThirdReimbursementInstitution::A(_)));
let field = Field55ThirdReimbursementInstitution::parse("PARIS BRANCH").unwrap();
assert!(matches!(field, Field55ThirdReimbursementInstitution::B(_)));
let field =
Field55ThirdReimbursementInstitution::parse("BANK NAME\nADDRESS LINE 1\nCITY").unwrap();
assert!(matches!(field, Field55ThirdReimbursementInstitution::D(_)));
}
}