use serde::{Deserialize, Serialize};
use std::any::Any;
use std::fmt::{self, Display, Formatter};
use thiserror::Error;
pub type Result<T> = std::result::Result<T, ParseError>;
pub type SwiftValidationResult<T> = std::result::Result<T, SwiftValidationError>;
#[derive(Debug, Serialize, Deserialize)]
pub struct ParseErrorCollection {
pub errors: Vec<ParseError>,
#[serde(skip)]
pub partial_result: Option<Box<dyn Any>>,
}
#[derive(Debug, Clone)]
pub struct FieldParseResult<T> {
pub value: Option<T>,
pub error: Option<ParseError>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ParseResult<T> {
Success(T),
PartialSuccess(T, Vec<ParseError>),
Failure(Vec<ParseError>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParserConfig {
pub fail_fast: bool,
pub validate_optional_fields: bool,
pub collect_all_errors: bool,
}
impl Default for ParserConfig {
fn default() -> Self {
Self {
fail_fast: false,
validate_optional_fields: true,
collect_all_errors: true,
}
}
}
impl Display for ParseErrorCollection {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
writeln!(f, "Found {} parsing errors:", self.errors.len())?;
for (idx, error) in self.errors.iter().enumerate() {
writeln!(f, "\n{}. {}", idx + 1, error)?;
}
Ok(())
}
}
impl std::error::Error for ParseErrorCollection {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InvalidFieldFormatError {
pub field_tag: String,
pub component_name: String,
pub value: String,
pub format_spec: String,
pub position: Option<usize>,
pub inner_error: String,
}
#[derive(Error, Debug, Clone, Serialize, Deserialize)]
pub enum ParseError {
#[error("Wrong message type: expected {expected}, got {actual}")]
WrongMessageType { expected: String, actual: String },
#[error("Unsupported message type: {message_type}")]
UnsupportedMessageType { message_type: String },
#[error("Field validation failed: {errors:?}")]
ValidationFailed { errors: Vec<ValidationError> },
#[error("IO error: {message}")]
IoError { message: String },
#[error(transparent)]
SwiftValidation(Box<SwiftValidationError>),
#[error("Serialization error: {message}")]
SerializationError { message: String },
#[error("Invalid message format: {message}")]
InvalidFormat { message: String },
#[error("Invalid field format - Field: {}, Component: {}, Value: '{}', Expected: {}", .0.field_tag, .0.component_name, .0.value, .0.format_spec)]
InvalidFieldFormat(Box<InvalidFieldFormatError>),
#[error("Missing required field {field_tag} ({field_name}) in {message_type}")]
MissingRequiredField {
field_tag: String,
field_name: String,
message_type: String,
position_in_block4: Option<usize>,
},
#[error(
"Failed to parse field {field_tag} of type {field_type} at line {position}: {original_error}"
)]
FieldParsingFailed {
field_tag: String,
field_type: String,
position: usize,
original_error: String,
},
#[error(
"Component parse error in field {field_tag}: {component_name} (index {component_index})"
)]
ComponentParseError {
field_tag: String,
component_index: usize,
component_name: String,
expected_format: String,
actual_value: String,
},
#[error("Invalid block {block} structure: {message}")]
InvalidBlockStructure {
block: String,
message: String,
},
#[error("Multiple parsing errors found ({} errors)", .0.len())]
MultipleErrors(Vec<ParseError>),
}
#[derive(Error, Debug, Clone, Serialize, Deserialize)]
pub enum ValidationError {
#[error("Field {field_tag} format validation failed: {message}")]
FormatValidation { field_tag: String, message: String },
#[error("Field {field_tag} length validation failed: expected {expected}, got {actual}")]
LengthValidation {
field_tag: String,
expected: String,
actual: usize,
},
#[error("Field {field_tag} pattern validation failed: {message}")]
PatternValidation { field_tag: String, message: String },
#[error("Field {field_tag} value validation failed: {message}")]
ValueValidation { field_tag: String, message: String },
#[error("Business rule validation failed: {rule_name} - {message}")]
BusinessRuleValidation { rule_name: String, message: String },
}
#[derive(Error, Debug, Clone, Serialize, Deserialize)]
pub enum SwiftValidationError {
#[error(transparent)]
Format(Box<SwiftFormatError>),
#[error(transparent)]
Business(Box<SwiftBusinessError>),
#[error(transparent)]
Content(Box<SwiftContentError>),
#[error(transparent)]
Relation(Box<SwiftRelationError>),
#[error(transparent)]
General(Box<SwiftGeneralError>),
}
#[derive(Error, Debug, Clone, Serialize, Deserialize)]
#[error("Format Error {code}: Field {field} contains '{value}', expected {expected}. {message}")]
pub struct SwiftFormatError {
pub code: String,
pub field: String,
pub value: String,
pub expected: String,
pub message: String,
pub context: Option<String>,
}
#[derive(Error, Debug, Clone, Serialize, Deserialize)]
#[error("Business Rule Violation {code}: {message} (Field: {field})")]
pub struct SwiftBusinessError {
pub code: String,
pub field: String,
pub related_fields: Vec<String>,
pub message: String,
pub rule_description: String,
pub context: Option<String>,
}
#[derive(Error, Debug, Clone, Serialize, Deserialize)]
#[error("Content Validation Error {code}: {message} (Field: {field})")]
pub struct SwiftContentError {
pub code: String,
pub field: String,
pub content: String,
pub message: String,
pub requirements: String,
pub context: Option<String>,
}
#[derive(Error, Debug, Clone, Serialize, Deserialize)]
#[error("Relation Validation Error {code}: {message} (Field: {field})")]
pub struct SwiftRelationError {
pub code: String,
pub field: String,
pub related_fields: Vec<String>,
pub instruction_context: Option<String>,
pub message: String,
pub rule_description: String,
pub context: Option<String>,
}
#[derive(Error, Debug, Clone, Serialize, Deserialize)]
#[error("General Validation Error {code}: {message} (Field: {field})")]
pub struct SwiftGeneralError {
pub code: String,
pub field: String,
pub value: String,
pub message: String,
pub category: Option<String>,
pub context: Option<String>,
}
impl From<std::io::Error> for ParseError {
fn from(err: std::io::Error) -> Self {
ParseError::IoError {
message: err.to_string(),
}
}
}
impl ParseError {
pub fn debug_report(&self) -> String {
match self {
ParseError::InvalidFieldFormat(err) => {
format!(
"Field Parsing Error:\n\
├─ Field Tag: {}\n\
├─ Component: {}\n\
├─ Value: '{}'\n\
├─ Expected Format: {}\n\
├─ Position in Message: {}\n\
├─ Details: {}\n\
└─ Hint: Check SWIFT format specification for field {}",
err.field_tag,
err.component_name,
err.value,
err.format_spec,
err.position
.map_or("unknown".to_string(), |p| p.to_string()),
err.inner_error,
err.field_tag
)
}
ParseError::MissingRequiredField {
field_tag,
field_name,
message_type,
position_in_block4,
} => {
format!(
"Missing Required Field:\n\
├─ Field Tag: {}\n\
├─ Field Name: {}\n\
├─ Message Type: {}\n\
├─ Expected Position: {}\n\
└─ Hint: {} requires field {} to be present",
field_tag,
field_name,
message_type,
position_in_block4.map_or("unknown".to_string(), |p| p.to_string()),
message_type,
field_tag
)
}
ParseError::ComponentParseError {
field_tag,
component_index,
component_name,
expected_format,
actual_value,
} => {
format!(
"Component Parse Error:\n\
├─ Field Tag: {field_tag}\n\
├─ Component: {component_name} (index {component_index})\n\
├─ Expected Format: {expected_format}\n\
├─ Actual Value: '{actual_value}'\n\
└─ Hint: Component '{component_name}' must match format '{expected_format}'"
)
}
ParseError::FieldParsingFailed {
field_tag,
field_type,
position,
original_error,
} => {
let line_num = if *position > 0xFFFF {
position >> 16
} else {
*position
};
format!(
"Field Parsing Failed:\n\
├─ Field Tag: {field_tag}\n\
├─ Field Type: {field_type}\n\
├─ Line Number: {line_num}\n\
├─ Error: {original_error}\n\
└─ Hint: Check the field value matches the expected type"
)
}
ParseError::InvalidBlockStructure { block, message } => {
format!(
"Block Structure Error:\n\
├─ Block: {block}\n\
├─ Error: {message}\n\
└─ Hint: Ensure block {block} follows SWIFT message structure"
)
}
ParseError::MultipleErrors(errors) => {
let mut output = format!("Multiple Parsing Errors ({} total):\n", errors.len());
for (idx, error) in errors.iter().enumerate() {
output.push_str(&format!("\n{}. {}\n", idx + 1, error.debug_report()));
}
output
}
_ => format!("{self}"),
}
}
pub fn brief_message(&self) -> String {
match self {
ParseError::InvalidFieldFormat(err) => {
format!(
"Field {} component '{}' format error",
err.field_tag, err.component_name
)
}
ParseError::MissingRequiredField {
field_tag,
message_type,
..
} => {
format!("Required field {field_tag} missing in {message_type}")
}
ParseError::ComponentParseError {
field_tag,
component_name,
..
} => {
format!("Field {field_tag} component '{component_name}' parse error")
}
ParseError::FieldParsingFailed {
field_tag,
field_type,
position,
..
} => {
let line_num = if *position > 0xFFFF {
position >> 16
} else {
*position
};
format!("Field {field_tag} (type {field_type}) parsing failed at line {line_num}")
}
ParseError::InvalidBlockStructure { block, .. } => {
format!("Block {block} structure invalid")
}
ParseError::MultipleErrors(errors) => {
format!("{} parsing errors found", errors.len())
}
_ => self.to_string(),
}
}
pub fn format_with_context(&self, original_message: &str) -> String {
match self {
ParseError::FieldParsingFailed { position, .. } => {
let lines: Vec<&str> = original_message.lines().collect();
let line_num = if *position > 0xFFFF {
position >> 16
} else {
*position
};
let mut output = self.debug_report();
if line_num > 0 && line_num <= lines.len() {
output.push_str("\n\nContext:\n");
let start = line_num.saturating_sub(3);
let end = (line_num + 2).min(lines.len());
for (i, line) in lines.iter().enumerate().take(end).skip(start) {
if i == line_num - 1 {
output.push_str(&format!(">>> {} │ {}\n", i + 1, line));
} else {
output.push_str(&format!(" {} │ {}\n", i + 1, line));
}
}
}
output
}
ParseError::InvalidFieldFormat(err) if err.position.is_some() => {
let lines: Vec<&str> = original_message.lines().collect();
let pos = err.position.unwrap();
let line_num = pos >> 16;
let mut output = self.debug_report();
if line_num > 0 && line_num <= lines.len() {
output.push_str("\n\nContext:\n");
let start = line_num.saturating_sub(3);
let end = (line_num + 2).min(lines.len());
for (i, line) in lines.iter().enumerate().take(end).skip(start) {
if i == line_num - 1 {
output.push_str(&format!(">>> {} │ {}\n", i + 1, line));
} else {
output.push_str(&format!(" {} │ {}\n", i + 1, line));
}
}
}
output
}
_ => self.debug_report(),
}
}
}
impl From<serde_json::Error> for ParseError {
fn from(err: serde_json::Error) -> Self {
ParseError::SerializationError {
message: err.to_string(),
}
}
}
pub mod error_codes {
pub mod format {
pub const T08: &str = "T08"; pub const T26: &str = "T26"; pub const T27: &str = "T27"; pub const T28: &str = "T28"; pub const T29: &str = "T29"; pub const T40: &str = "T40"; pub const T43: &str = "T43"; pub const T45: &str = "T45"; pub const T50: &str = "T50"; pub const T52: &str = "T52"; pub const T56: &str = "T56"; pub const T73: &str = "T73"; }
pub mod business {
pub const C02: &str = "C02"; pub const C03: &str = "C03"; pub const C08: &str = "C08"; pub const C81: &str = "C81"; }
pub mod content {
pub const D17: &str = "D17"; pub const D18: &str = "D18"; pub const D19: &str = "D19"; pub const D20: &str = "D20"; pub const D22: &str = "D22"; pub const D49: &str = "D49"; pub const D50: &str = "D50"; pub const D51: &str = "D51"; pub const D75: &str = "D75"; pub const D79: &str = "D79"; pub const D93: &str = "D93"; }
pub mod relation {
pub const E01: &str = "E01"; pub const E02: &str = "E02"; pub const E03: &str = "E03"; pub const E04: &str = "E04"; pub const E05: &str = "E05"; pub const E06: &str = "E06"; pub const E07: &str = "E07"; pub const E09: &str = "E09"; pub const E10: &str = "E10"; pub const E13: &str = "E13"; pub const E15: &str = "E15"; pub const E16: &str = "E16"; pub const E17: &str = "E17"; pub const E18: &str = "E18"; pub const E44: &str = "E44"; pub const E45: &str = "E45"; }
pub mod general {
pub const G001: &str = "G001"; pub const G050: &str = "G050"; pub const G100: &str = "G100"; }
}
impl SwiftValidationError {
pub fn error_code(&self) -> &str {
match self {
SwiftValidationError::Format(err) => &err.code,
SwiftValidationError::Business(err) => &err.code,
SwiftValidationError::Content(err) => &err.code,
SwiftValidationError::Relation(err) => &err.code,
SwiftValidationError::General(err) => &err.code,
}
}
pub fn format_error(
code: &str,
field: &str,
value: &str,
expected: &str,
message: &str,
) -> Self {
SwiftValidationError::Format(Box::new(SwiftFormatError {
code: code.to_string(),
field: field.to_string(),
value: value.to_string(),
expected: expected.to_string(),
message: message.to_string(),
context: None,
}))
}
pub fn business_error(
code: &str,
field: &str,
related_fields: Vec<String>,
message: &str,
rule_description: &str,
) -> Self {
SwiftValidationError::Business(Box::new(SwiftBusinessError {
code: code.to_string(),
field: field.to_string(),
related_fields,
message: message.to_string(),
rule_description: rule_description.to_string(),
context: None,
}))
}
pub fn content_error(
code: &str,
field: &str,
content: &str,
message: &str,
requirements: &str,
) -> Self {
SwiftValidationError::Content(Box::new(SwiftContentError {
code: code.to_string(),
field: field.to_string(),
content: content.to_string(),
message: message.to_string(),
requirements: requirements.to_string(),
context: None,
}))
}
pub fn relation_error(
code: &str,
field: &str,
related_fields: Vec<String>,
message: &str,
rule_description: &str,
) -> Self {
SwiftValidationError::Relation(Box::new(SwiftRelationError {
code: code.to_string(),
field: field.to_string(),
related_fields,
instruction_context: None,
message: message.to_string(),
rule_description: rule_description.to_string(),
context: None,
}))
}
pub fn general_error(
code: &str,
field: &str,
value: &str,
message: &str,
category: Option<&str>,
) -> Self {
SwiftValidationError::General(Box::new(SwiftGeneralError {
code: code.to_string(),
field: field.to_string(),
value: value.to_string(),
message: message.to_string(),
category: category.map(|s| s.to_string()),
context: None,
}))
}
pub fn code(&self) -> &str {
match self {
SwiftValidationError::Format(err) => &err.code,
SwiftValidationError::Business(err) => &err.code,
SwiftValidationError::Content(err) => &err.code,
SwiftValidationError::Relation(err) => &err.code,
SwiftValidationError::General(err) => &err.code,
}
}
pub fn field(&self) -> &str {
match self {
SwiftValidationError::Format(err) => &err.field,
SwiftValidationError::Business(err) => &err.field,
SwiftValidationError::Content(err) => &err.field,
SwiftValidationError::Relation(err) => &err.field,
SwiftValidationError::General(err) => &err.field,
}
}
pub fn message(&self) -> &str {
match self {
SwiftValidationError::Format(err) => &err.message,
SwiftValidationError::Business(err) => &err.message,
SwiftValidationError::Content(err) => &err.message,
SwiftValidationError::Relation(err) => &err.message,
SwiftValidationError::General(err) => &err.message,
}
}
}
impl From<SwiftValidationError> for ValidationError {
fn from(swift_error: SwiftValidationError) -> Self {
match swift_error {
SwiftValidationError::Format(err) => ValidationError::FormatValidation {
field_tag: err.field,
message: format!("{}: {}", err.code, err.message),
},
SwiftValidationError::Business(err) => ValidationError::BusinessRuleValidation {
rule_name: err.code,
message: err.message,
},
SwiftValidationError::Content(err) => ValidationError::ValueValidation {
field_tag: err.field,
message: format!("{}: {}", err.code, err.message),
},
SwiftValidationError::Relation(err) => ValidationError::BusinessRuleValidation {
rule_name: err.code,
message: err.message,
},
SwiftValidationError::General(err) => ValidationError::FormatValidation {
field_tag: err.field,
message: format!("{}: {}", err.code, err.message),
},
}
}
}
impl From<SwiftValidationError> for ParseError {
fn from(validation_error: SwiftValidationError) -> Self {
ParseError::SwiftValidation(Box::new(validation_error))
}
}
impl From<ValidationError> for SwiftValidationError {
fn from(validation_error: ValidationError) -> Self {
match validation_error {
ValidationError::FormatValidation { field_tag, message } => {
SwiftValidationError::format_error("T00", &field_tag, "", "", &message)
}
ValidationError::LengthValidation {
field_tag,
expected,
actual,
} => SwiftValidationError::format_error(
"T00",
&field_tag,
&actual.to_string(),
&expected,
"Length validation failed",
),
ValidationError::PatternValidation { field_tag, message } => {
SwiftValidationError::format_error("T00", &field_tag, "", "", &message)
}
ValidationError::ValueValidation { field_tag, message } => {
SwiftValidationError::content_error("D00", &field_tag, "", &message, "")
}
ValidationError::BusinessRuleValidation { rule_name, message } => {
SwiftValidationError::business_error(&rule_name, "", vec![], &message, "")
}
}
}
}