use crate::errors::SwiftValidationError;
use crate::fields::*;
use crate::parser::utils::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub struct MT950 {
#[serde(rename = "20")]
pub field_20: Field20,
#[serde(rename = "25")]
pub field_25: Field25NoOption,
#[serde(rename = "28C")]
pub field_28c: Field28C,
#[serde(rename = "60")]
pub field_60: Field60,
#[serde(rename = "61", skip_serializing_if = "Option::is_none")]
pub field_61: Option<Vec<Field61>>,
#[serde(rename = "62")]
pub field_62: Field62,
#[serde(rename = "64", skip_serializing_if = "Option::is_none")]
pub field_64: Option<Field64>,
}
impl MT950 {
pub fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
let mut parser = crate::parser::MessageParser::new(block4, "950");
let field_20 = parser.parse_field::<Field20>("20")?;
let field_25 = parser.parse_field::<Field25NoOption>("25")?;
let field_28c = parser.parse_field::<Field28C>("28C")?;
let field_60 = if parser.detect_field("60F") {
Field60::F(parser.parse_field::<Field60F>("60F")?)
} else if parser.detect_field("60M") {
Field60::M(parser.parse_field::<Field60M>("60M")?)
} else if parser.detect_field("60") {
parser.parse_field::<Field60>("60")?
} else {
return Err(crate::errors::ParseError::InvalidFormat {
message: "MT950: Missing required field 60 (opening balance)".to_string(),
});
};
parser = parser.with_duplicates(true);
let mut field_61_vec = Vec::new();
while parser.detect_field("61") {
if let Ok(field) = parser.parse_field::<Field61>("61") {
field_61_vec.push(field);
} else {
break;
}
}
let field_61 = if field_61_vec.is_empty() {
None
} else {
Some(field_61_vec)
};
let field_62 = if parser.detect_field("62F") {
Field62::F(parser.parse_field::<Field62F>("62F")?)
} else if parser.detect_field("62M") {
Field62::M(parser.parse_field::<Field62M>("62M")?)
} else if parser.detect_field("62") {
parser.parse_field::<Field62>("62")?
} else {
return Err(crate::errors::ParseError::InvalidFormat {
message: "MT950: Missing required field 62 (closing balance)".to_string(),
});
};
let field_64 = parser.parse_optional_field::<Field64>("64")?;
Ok(MT950 {
field_20,
field_25,
field_28c,
field_60,
field_61,
field_62,
field_64,
})
}
fn get_field_60_currency_prefix(&self) -> &str {
match &self.field_60 {
Field60::F(field) => &field.currency[0..2],
Field60::M(field) => &field.currency[0..2],
}
}
fn get_field_62_currency_prefix(&self) -> &str {
match &self.field_62 {
Field62::F(field) => &field.currency[0..2],
Field62::M(field) => &field.currency[0..2],
}
}
fn get_field_60_currency(&self) -> &str {
match &self.field_60 {
Field60::F(field) => &field.currency,
Field60::M(field) => &field.currency,
}
}
fn get_field_62_currency(&self) -> &str {
match &self.field_62 {
Field62::F(field) => &field.currency,
Field62::M(field) => &field.currency,
}
}
fn validate_c1_currency_consistency(&self) -> Vec<SwiftValidationError> {
let mut errors = Vec::new();
let base_currency_prefix = self.get_field_60_currency_prefix();
let base_currency = self.get_field_60_currency();
let field_62_prefix = self.get_field_62_currency_prefix();
let field_62_currency = self.get_field_62_currency();
if field_62_prefix != base_currency_prefix {
errors.push(SwiftValidationError::business_error(
"C27",
"62a",
vec!["60a".to_string()],
&format!(
"Currency code mismatch: field 62a currency '{}' (prefix '{}') must have the same first two characters as field 60a currency '{}' (prefix '{}')",
field_62_currency, field_62_prefix, base_currency, base_currency_prefix
),
"The first two characters of the three character currency code in fields 60a, 62a and 64 must be the same",
));
}
if let Some(ref field_64) = self.field_64 {
let field_64_prefix = &field_64.currency[0..2];
if field_64_prefix != base_currency_prefix {
errors.push(SwiftValidationError::business_error(
"C27",
"64",
vec!["60a".to_string(), "62a".to_string()],
&format!(
"Currency code mismatch: field 64 currency '{}' (prefix '{}') must have the same first two characters as field 60a currency '{}' (prefix '{}')",
field_64.currency, field_64_prefix, base_currency, base_currency_prefix
),
"The first two characters of the three character currency code in fields 60a, 62a and 64 must be the same",
));
}
}
errors
}
pub fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
let mut all_errors = Vec::new();
let c1_errors = self.validate_c1_currency_consistency();
all_errors.extend(c1_errors);
if stop_on_first_error && !all_errors.is_empty() {
return all_errors;
}
all_errors
}
}
impl crate::traits::SwiftMessageBody for MT950 {
fn message_type() -> &'static str {
"950"
}
fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
Self::parse_from_block4(block4)
}
fn to_mt_string(&self) -> String {
let mut result = String::new();
append_field(&mut result, &self.field_20);
append_field(&mut result, &self.field_25);
append_field(&mut result, &self.field_28c);
append_field(&mut result, &self.field_60);
append_vec_field(&mut result, &self.field_61);
append_field(&mut result, &self.field_62);
append_optional_field(&mut result, &self.field_64);
finalize_mt_string(result, false)
}
fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
MT950::validate_network_rules(self, stop_on_first_error)
}
}