use super::swift_utils::{parse_swift_digits, split_at_first};
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 Field28 {
pub statement_number: u32,
pub sequence_number: Option<u8>,
}
impl SwiftField for Field28 {
fn parse(input: &str) -> crate::Result<Self>
where
Self: Sized,
{
let (statement_str, sequence_str) = split_at_first(input, '/');
if statement_str.len() > 5 {
return Err(ParseError::InvalidFormat {
message: "Statement number must be at most 5 digits".to_string(),
});
}
parse_swift_digits(&statement_str, "Statement number")?;
let statement_number: u32 =
statement_str
.parse()
.map_err(|_| ParseError::InvalidFormat {
message: "Invalid statement number".to_string(),
})?;
let sequence_number = if let Some(seq_str) = sequence_str {
if seq_str.len() > 2 {
return Err(ParseError::InvalidFormat {
message: "Sequence number must be at most 2 digits".to_string(),
});
}
parse_swift_digits(&seq_str, "Sequence number")?;
let seq: u8 = seq_str.parse().map_err(|_| ParseError::InvalidFormat {
message: "Invalid sequence number".to_string(),
})?;
Some(seq)
} else {
None
};
Ok(Field28 {
statement_number,
sequence_number,
})
}
fn to_swift_string(&self) -> String {
if let Some(seq) = self.sequence_number {
format!(":28:{}/{:02}", self.statement_number, seq)
} else {
format!(":28:{}", self.statement_number)
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub struct Field28C {
pub statement_number: u32,
pub sequence_number: Option<u32>,
}
impl SwiftField for Field28C {
fn parse(input: &str) -> crate::Result<Self>
where
Self: Sized,
{
let (statement_str, sequence_str) = split_at_first(input, '/');
if statement_str.len() > 5 {
return Err(ParseError::InvalidFormat {
message: "Statement number must be at most 5 digits".to_string(),
});
}
parse_swift_digits(&statement_str, "Statement number")?;
let statement_number: u32 =
statement_str
.parse()
.map_err(|_| ParseError::InvalidFormat {
message: "Invalid statement number".to_string(),
})?;
let sequence_number = if let Some(seq_str) = sequence_str {
if seq_str.len() > 5 {
return Err(ParseError::InvalidFormat {
message: "Sequence number must be at most 5 digits".to_string(),
});
}
parse_swift_digits(&seq_str, "Sequence number")?;
let seq: u32 = seq_str.parse().map_err(|_| ParseError::InvalidFormat {
message: "Invalid sequence number".to_string(),
})?;
Some(seq)
} else {
None
};
Ok(Field28C {
statement_number,
sequence_number,
})
}
fn to_swift_string(&self) -> String {
if let Some(seq) = self.sequence_number {
format!(":28C:{}/{}", self.statement_number, seq)
} else {
format!(":28C:{}", self.statement_number)
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub struct Field28D {
pub index: u32,
pub total: u32,
}
impl SwiftField for Field28D {
fn parse(input: &str) -> crate::Result<Self>
where
Self: Sized,
{
let (index_str, total_str) = split_at_first(input, '/');
if index_str.len() > 5 {
return Err(ParseError::InvalidFormat {
message: "Index number must be at most 5 digits".to_string(),
});
}
parse_swift_digits(&index_str, "Index number")?;
let index: u32 = index_str.parse().map_err(|_| ParseError::InvalidFormat {
message: "Invalid index number".to_string(),
})?;
let total_str = total_str.ok_or_else(|| ParseError::InvalidFormat {
message: "Field28D requires both index and total separated by '/'".to_string(),
})?;
if total_str.len() > 5 {
return Err(ParseError::InvalidFormat {
message: "Total count must be at most 5 digits".to_string(),
});
}
parse_swift_digits(&total_str, "Total count")?;
let total: u32 = total_str.parse().map_err(|_| ParseError::InvalidFormat {
message: "Invalid total count".to_string(),
})?;
if index > total {
return Err(ParseError::InvalidFormat {
message: format!("Index {} cannot exceed total {}", index, total),
});
}
if index == 0 || total == 0 {
return Err(ParseError::InvalidFormat {
message: "Index and total must be greater than zero".to_string(),
});
}
Ok(Field28D { index, total })
}
fn to_swift_string(&self) -> String {
format!(":28D:{:03}/{:03}", self.index, self.total)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_field28_parse() {
let field = Field28::parse("12345/67").unwrap();
assert_eq!(field.statement_number, 12345);
assert_eq!(field.sequence_number, Some(67));
let field = Field28::parse("12345").unwrap();
assert_eq!(field.statement_number, 12345);
assert_eq!(field.sequence_number, None);
let field = Field28::parse("123/5").unwrap();
assert_eq!(field.statement_number, 123);
assert_eq!(field.sequence_number, Some(5));
}
#[test]
fn test_field28c_parse() {
let field = Field28C::parse("12345/99999").unwrap();
assert_eq!(field.statement_number, 12345);
assert_eq!(field.sequence_number, Some(99999));
let field = Field28C::parse("12345").unwrap();
assert_eq!(field.statement_number, 12345);
assert_eq!(field.sequence_number, None);
}
#[test]
fn test_field28d_parse() {
let field = Field28D::parse("001/010").unwrap();
assert_eq!(field.index, 1);
assert_eq!(field.total, 10);
let field = Field28D::parse("5/5").unwrap();
assert_eq!(field.index, 5);
assert_eq!(field.total, 5);
}
#[test]
fn test_field28_to_swift_string() {
let field = Field28 {
statement_number: 12345,
sequence_number: Some(67),
};
assert_eq!(field.to_swift_string(), ":28:12345/67");
let field = Field28 {
statement_number: 12345,
sequence_number: None,
};
assert_eq!(field.to_swift_string(), ":28:12345");
}
#[test]
fn test_field28c_to_swift_string() {
let field = Field28C {
statement_number: 12345,
sequence_number: Some(99999),
};
assert_eq!(field.to_swift_string(), ":28C:12345/99999");
}
#[test]
fn test_field28d_to_swift_string() {
let field = Field28D {
index: 1,
total: 10,
};
assert_eq!(field.to_swift_string(), ":28D:001/010");
}
#[test]
fn test_field28d_validation_errors() {
assert!(Field28D::parse("11/10").is_err());
assert!(Field28D::parse("0/10").is_err());
assert!(Field28D::parse("1/0").is_err());
assert!(Field28D::parse("5").is_err());
}
}