use std::collections::{HashMap, HashSet};
use crate::errors::{ParseError, Result, SwiftValidationError};
use crate::headers::{ApplicationHeader, BasicHeader, Trailer, UserHeader};
use crate::messages::{
MT101, MT103, MT104, MT107, MT110, MT111, MT112, MT190, MT191, MT192, MT196, MT199, MT200,
MT202, MT204, MT205, MT210, MT290, MT291, MT292, MT296, MT299, MT900, MT910, MT920, MT935,
MT940, MT941, MT942, MT950,
};
use crate::swift_error_codes::t_series;
use crate::{ParsedSwiftMessage, SwiftMessage, SwiftMessageBody};
#[derive(Debug, Clone)]
pub struct ParsingContext {
pub current_field: Option<String>,
pub current_component: Option<String>,
pub message_type: String,
pub original_message: String,
}
impl ParsingContext {
pub fn new(message_type: String, original_message: String) -> Self {
Self {
current_field: None,
current_component: None,
message_type,
original_message,
}
}
pub fn with_field(&self, field: String) -> Self {
let mut ctx = self.clone();
ctx.current_field = Some(field);
ctx.current_component = None;
ctx
}
pub fn with_component(&self, component: String) -> Self {
let mut ctx = self.clone();
ctx.current_component = Some(component);
ctx
}
}
#[derive(Debug, Clone)]
pub struct FieldConsumptionTracker {
consumed_indices: HashMap<String, HashSet<usize>>,
}
impl Default for FieldConsumptionTracker {
fn default() -> Self {
Self::new()
}
}
impl FieldConsumptionTracker {
pub fn new() -> Self {
Self {
consumed_indices: HashMap::new(),
}
}
pub fn mark_consumed(&mut self, tag: &str, index: usize) {
use std::collections::hash_map::Entry;
match self.consumed_indices.entry(tag.to_string()) {
Entry::Occupied(mut e) => {
e.get_mut().insert(index);
}
Entry::Vacant(e) => {
let mut set = HashSet::new();
set.insert(index);
e.insert(set);
}
}
}
pub fn get_next_available<'a>(
&self,
tag: &str,
values: &'a [(String, usize)],
) -> Option<(&'a str, usize)> {
let consumed_set = self.consumed_indices.get(tag);
values
.iter()
.find(|(_, pos)| consumed_set.is_none_or(|set| !set.contains(pos)))
.map(|(value, pos)| (value.as_str(), *pos))
}
}
#[derive(Debug, Clone)]
enum FieldRoutingStrategy {
InstructingParty,
CreditorParty,
}
impl FieldRoutingStrategy {
fn get_preferred_variants(&self, base_tag: &str) -> Option<Vec<&'static str>> {
match (self, base_tag) {
(FieldRoutingStrategy::InstructingParty, "50") => Some(vec!["C", "L"]),
(FieldRoutingStrategy::CreditorParty, "50") => Some(vec!["A", "F", "K"]),
_ => None,
}
}
}
fn apply_field50_routing_strategy<'a>(
mut candidates: Vec<(&'a String, &'a Vec<(String, usize)>)>,
tracker: &FieldConsumptionTracker,
base_tag: &str,
) -> Vec<(&'a String, &'a Vec<(String, usize)>)> {
let consumed_field50_count = candidates
.iter()
.filter(|(tag, _)| {
tracker
.consumed_indices
.get(*tag)
.is_some_and(|set| !set.is_empty())
})
.count();
#[cfg(debug_assertions)]
eprintln!(
"DEBUG: Field50 routing strategy - consumed_count={}, candidates={:?}",
consumed_field50_count,
candidates
.iter()
.map(|(tag, _)| tag.as_str())
.collect::<Vec<_>>()
);
let strategy = if consumed_field50_count == 0 {
FieldRoutingStrategy::InstructingParty
} else {
FieldRoutingStrategy::CreditorParty
};
if let Some(preferred_variants) = strategy.get_preferred_variants(base_tag) {
candidates.sort_by_key(|(tag, _)| {
let variant_char = tag.chars().last().unwrap_or(' ');
let variant_str = variant_char.to_string();
let is_preferred = preferred_variants.contains(&variant_str.as_str());
if is_preferred {
0 } else {
1 }
});
#[cfg(debug_assertions)]
eprintln!(
"DEBUG: Field50 routing - strategy={:?}, preferred_variants={:?}, reordered_candidates={:?}",
strategy,
preferred_variants,
candidates
.iter()
.map(|(tag, _)| tag.as_str())
.collect::<Vec<_>>()
);
}
candidates
}
pub fn find_field_with_variant_sequential_numbered(
fields: &HashMap<String, Vec<(String, usize)>>,
base_tag: &str,
tracker: &mut FieldConsumptionTracker,
valid_variants: Option<Vec<&str>>,
_numbered_tag: &str,
) -> Option<(String, Option<String>, usize)> {
#[cfg(debug_assertions)]
eprintln!(
"DEBUG: find_field_with_variant_sequential_numbered for tag={}, base={}, variants={:?}",
_numbered_tag, base_tag, valid_variants
);
find_field_with_variant_sequential_constrained(
fields,
base_tag,
tracker,
valid_variants.as_deref(),
)
}
pub fn find_field_with_variant_sequential_constrained(
fields: &HashMap<String, Vec<(String, usize)>>,
base_tag: &str,
tracker: &mut FieldConsumptionTracker,
valid_variants: Option<&[&str]>,
) -> Option<(String, Option<String>, usize)> {
#[cfg(debug_assertions)]
{
eprintln!(
"DEBUG: find_field_with_variant called for base_tag={}, valid_variants={:?}",
base_tag, valid_variants
);
eprintln!(
" Available fields: {:?}",
fields.keys().collect::<Vec<_>>()
);
if base_tag == "50" {
eprintln!(
"DEBUG: Field50 routing - base_tag={}, constraints={:?}",
base_tag, valid_variants
);
eprintln!(
"DEBUG: Available Field50 variants: {:?}",
fields
.keys()
.filter(|k| k.starts_with("50") && k.len() == 3)
.collect::<Vec<_>>()
);
}
}
if let Some(values) = fields.get(base_tag)
&& let Some((value, pos)) = tracker.get_next_available(base_tag, values)
{
#[cfg(debug_assertions)]
{
eprintln!(
"DEBUG: Found exact match for base_tag={}, marking as consumed at pos={}",
base_tag, pos
);
if base_tag == "90D" || base_tag == "86" {
eprintln!(
"DEBUG: Returning value for {}: '{}'",
base_tag,
if value.len() > 50 {
&value[..50]
} else {
value
}
);
}
}
tracker.mark_consumed(base_tag, pos);
return Some((value.to_string(), None, pos));
}
let mut variant_candidates: Vec<(&String, &Vec<(String, usize)>)> = fields
.iter()
.filter(|(tag, _)| {
tag.starts_with(base_tag)
&& tag.len() == base_tag.len() + 1
&& tag
.chars()
.last()
.is_some_and(|c| c.is_ascii_alphabetic() && c.is_ascii_uppercase())
})
.collect();
if base_tag == "50" && valid_variants.is_some() {
variant_candidates = apply_field50_routing_strategy(variant_candidates, tracker, base_tag);
}
variant_candidates.sort_by_key(|(tag, values)| {
values
.iter()
.filter(|(_, pos)| {
tracker
.consumed_indices
.get(*tag)
.is_none_or(|set| !set.contains(pos))
})
.map(|(_, pos)| *pos)
.min()
.unwrap_or(usize::MAX)
});
for (tag, values) in variant_candidates {
let variant_char = tag.chars().last().unwrap();
let variant_str = variant_char.to_string();
if let Some(valid) = valid_variants
&& !valid.contains(&variant_str.as_str())
{
#[cfg(debug_assertions)]
eprintln!(
"DEBUG: Skipping field {} with variant {} - not in valid variants {:?}",
tag, variant_str, valid
);
continue; }
if let Some((value, pos)) = tracker.get_next_available(tag, values) {
#[cfg(debug_assertions)]
eprintln!(
"DEBUG: Found field {} with variant {} for base tag {}",
tag, variant_str, base_tag
);
tracker.mark_consumed(tag, pos);
return Some((value.to_string(), Some(variant_str), pos));
}
}
None
}
pub struct SwiftParser {}
impl Default for SwiftParser {
fn default() -> Self {
Self::new()
}
}
impl SwiftParser {
pub fn new() -> Self {
Self {}
}
pub fn parse_with_errors<T: SwiftMessageBody>(
&self,
raw_message: &str,
) -> Result<crate::errors::ParseResult<SwiftMessage<T>>> {
let block1 = Self::extract_block(raw_message, 1)?;
let block2 = Self::extract_block(raw_message, 2)?;
let block3 = Self::extract_block(raw_message, 3)?;
let block4 = Self::extract_block(raw_message, 4)?;
let block5 = Self::extract_block(raw_message, 5)?;
let basic_header = BasicHeader::parse(&block1.unwrap_or_default())?;
let application_header = ApplicationHeader::parse(&block2.unwrap_or_default())?;
let user_header = block3.map(|b| UserHeader::parse(&b)).transpose()?;
let trailer = block5.map(|b| Trailer::parse(&b)).transpose()?;
let message_type = application_header.message_type().to_string();
if message_type != T::message_type() {
return Err(ParseError::SwiftValidation(Box::new(
SwiftValidationError::format_error(
t_series::T03,
"MESSAGE_TYPE",
&message_type,
T::message_type(),
&format!(
"Message type mismatch: expected {}, got {}",
T::message_type(),
message_type
),
),
)));
}
let fields = T::parse_from_block4(&block4.unwrap_or_default())?;
Ok(crate::errors::ParseResult::Success(SwiftMessage {
basic_header,
application_header,
user_header,
trailer,
message_type,
fields,
}))
}
pub fn parse<T: SwiftMessageBody>(raw_message: &str) -> Result<SwiftMessage<T>> {
Self::new().parse_message(raw_message)
}
pub fn parse_message<T: SwiftMessageBody>(&self, raw_message: &str) -> Result<SwiftMessage<T>> {
let block1 = Self::extract_block(raw_message, 1)?;
let block2 = Self::extract_block(raw_message, 2)?;
let block3 = Self::extract_block(raw_message, 3)?;
let block4 = Self::extract_block(raw_message, 4)?;
let block5 = Self::extract_block(raw_message, 5)?;
let basic_header = BasicHeader::parse(&block1.unwrap_or_default())?;
let application_header = ApplicationHeader::parse(&block2.unwrap_or_default())?;
let user_header = block3.map(|b| UserHeader::parse(&b)).transpose()?;
let trailer = block5.map(|b| Trailer::parse(&b)).transpose()?;
let message_type = application_header.message_type().to_string();
if message_type != T::message_type() {
return Err(ParseError::SwiftValidation(Box::new(
SwiftValidationError::format_error(
t_series::T03,
"MESSAGE_TYPE",
&message_type,
T::message_type(),
&format!(
"Message type mismatch: expected {}, got {}",
T::message_type(),
message_type
),
),
)));
}
let fields = T::parse_from_block4(&block4.unwrap_or_default())?;
Ok(SwiftMessage {
basic_header,
application_header,
user_header,
trailer,
message_type,
fields,
})
}
pub fn parse_auto(raw_message: &str) -> Result<ParsedSwiftMessage> {
Self::new().parse_message_auto(raw_message)
}
pub fn parse_message_auto(&self, raw_message: &str) -> Result<ParsedSwiftMessage> {
let block2 = Self::extract_block(raw_message, 2)?;
let application_header = ApplicationHeader::parse(&block2.unwrap_or_default())?;
let message_type = application_header.message_type();
match message_type {
"101" => {
let parsed = self.parse_message::<MT101>(raw_message)?;
Ok(ParsedSwiftMessage::MT101(Box::new(parsed)))
}
"103" => {
let parsed = self.parse_message::<MT103>(raw_message)?;
Ok(ParsedSwiftMessage::MT103(Box::new(parsed)))
}
"104" => {
let parsed = self.parse_message::<MT104>(raw_message)?;
Ok(ParsedSwiftMessage::MT104(Box::new(parsed)))
}
"107" => {
let parsed = self.parse_message::<MT107>(raw_message)?;
Ok(ParsedSwiftMessage::MT107(Box::new(parsed)))
}
"110" => {
let parsed = self.parse_message::<MT110>(raw_message)?;
Ok(ParsedSwiftMessage::MT110(Box::new(parsed)))
}
"111" => {
let parsed = self.parse_message::<MT111>(raw_message)?;
Ok(ParsedSwiftMessage::MT111(Box::new(parsed)))
}
"112" => {
let parsed = self.parse_message::<MT112>(raw_message)?;
Ok(ParsedSwiftMessage::MT112(Box::new(parsed)))
}
"190" => {
let parsed = self.parse_message::<MT190>(raw_message)?;
Ok(ParsedSwiftMessage::MT190(Box::new(parsed)))
}
"191" => {
let parsed = self.parse_message::<MT191>(raw_message)?;
Ok(ParsedSwiftMessage::MT191(Box::new(parsed)))
}
"200" => {
let parsed = self.parse_message::<MT200>(raw_message)?;
Ok(ParsedSwiftMessage::MT200(Box::new(parsed)))
}
"202" => {
let parsed = self.parse_message::<MT202>(raw_message)?;
Ok(ParsedSwiftMessage::MT202(Box::new(parsed)))
}
"204" => {
let parsed = self.parse_message::<MT204>(raw_message)?;
Ok(ParsedSwiftMessage::MT204(Box::new(parsed)))
}
"205" => {
let parsed = self.parse_message::<MT205>(raw_message)?;
Ok(ParsedSwiftMessage::MT205(Box::new(parsed)))
}
"210" => {
let parsed = self.parse_message::<MT210>(raw_message)?;
Ok(ParsedSwiftMessage::MT210(Box::new(parsed)))
}
"290" => {
let parsed = self.parse_message::<MT290>(raw_message)?;
Ok(ParsedSwiftMessage::MT290(Box::new(parsed)))
}
"291" => {
let parsed = self.parse_message::<MT291>(raw_message)?;
Ok(ParsedSwiftMessage::MT291(Box::new(parsed)))
}
"900" => {
let parsed = self.parse_message::<MT900>(raw_message)?;
Ok(ParsedSwiftMessage::MT900(Box::new(parsed)))
}
"910" => {
let parsed = self.parse_message::<MT910>(raw_message)?;
Ok(ParsedSwiftMessage::MT910(Box::new(parsed)))
}
"920" => {
let parsed = self.parse_message::<MT920>(raw_message)?;
Ok(ParsedSwiftMessage::MT920(Box::new(parsed)))
}
"935" => {
let parsed = self.parse_message::<MT935>(raw_message)?;
Ok(ParsedSwiftMessage::MT935(Box::new(parsed)))
}
"940" => {
let parsed = self.parse_message::<MT940>(raw_message)?;
Ok(ParsedSwiftMessage::MT940(Box::new(parsed)))
}
"941" => {
let parsed = self.parse_message::<MT941>(raw_message)?;
Ok(ParsedSwiftMessage::MT941(Box::new(parsed)))
}
"942" => {
let parsed = self.parse_message::<MT942>(raw_message)?;
Ok(ParsedSwiftMessage::MT942(Box::new(parsed)))
}
"950" => {
let parsed = self.parse_message::<MT950>(raw_message)?;
Ok(ParsedSwiftMessage::MT950(Box::new(parsed)))
}
"192" => {
let parsed = self.parse_message::<MT192>(raw_message)?;
Ok(ParsedSwiftMessage::MT192(Box::new(parsed)))
}
"196" => {
let parsed = self.parse_message::<MT196>(raw_message)?;
Ok(ParsedSwiftMessage::MT196(Box::new(parsed)))
}
"292" => {
let parsed = self.parse_message::<MT292>(raw_message)?;
Ok(ParsedSwiftMessage::MT292(Box::new(parsed)))
}
"296" => {
let parsed = self.parse_message::<MT296>(raw_message)?;
Ok(ParsedSwiftMessage::MT296(Box::new(parsed)))
}
"199" => {
let parsed = self.parse_message::<MT199>(raw_message)?;
Ok(ParsedSwiftMessage::MT199(Box::new(parsed)))
}
"299" => {
let parsed = self.parse_message::<MT299>(raw_message)?;
Ok(ParsedSwiftMessage::MT299(Box::new(parsed)))
}
_ => Err(ParseError::UnsupportedMessageType {
message_type: message_type.to_string(),
}),
}
}
pub fn extract_block(raw_message: &str, block_index: u8) -> Result<Option<String>> {
if !(1..=5).contains(&block_index) {
return Err(ParseError::SwiftValidation(Box::new(
crate::errors::SwiftValidationError::format_error(
crate::swift_error_codes::t_series::T01,
"BLOCK_INDEX",
&block_index.to_string(),
"1-5",
&format!("Invalid block index: {block_index}"),
),
)));
}
let block_marker = format!("{{{block_index}:");
if let Some(start) = raw_message.find(&block_marker) {
let content_start = start + block_marker.len();
match block_index {
1 | 2 => {
if let Some(end) = raw_message[start..].find('}') {
let end = start + end;
Ok(Some(raw_message[content_start..end].to_string()))
} else {
Ok(None)
}
}
3 | 5 => {
if let Some(end) = Self::find_matching_brace(&raw_message[start..]) {
let end = start + end;
Ok(Some(raw_message[content_start..end].to_string()))
} else {
Ok(None)
}
}
4 => {
if let Some(end) = raw_message[start..].find("-}") {
let end = start + end;
Ok(Some(raw_message[content_start..end].to_string()))
} else {
Ok(None)
}
}
_ => Err(ParseError::SwiftValidation(Box::new(
crate::errors::SwiftValidationError::format_error(
crate::swift_error_codes::t_series::T02,
"BLOCK",
&block_index.to_string(),
"1-5",
&format!("Invalid block index: {block_index}"),
),
))),
}
} else {
Ok(None)
}
}
fn find_matching_brace(text: &str) -> Option<usize> {
let mut chars = text.char_indices();
let mut brace_count = if let Some((_, '{')) = chars.next() {
1
} else {
return None;
};
for (i, ch) in chars {
match ch {
'{' => brace_count += 1,
'}' => {
brace_count -= 1;
if brace_count == 0 {
return Some(i);
}
}
_ => {}
}
}
None
}
}
fn reconstruct_block4_from_fields(fields: &HashMap<String, Vec<(String, usize)>>) -> String {
let mut all_fields: Vec<(&str, &str, usize)> = Vec::new();
for (tag, values) in fields {
for (value, pos) in values {
all_fields.push((tag, value, *pos));
}
}
all_fields.sort_by_key(|(_, _, pos)| *pos);
let mut result = String::new();
for (tag, value, _) in all_fields {
let clean_value = if value.starts_with(&format!(":{tag}:")) {
&value[tag.len() + 2..]
} else if let Some(stripped) = value.strip_prefix(':') {
if let Some(second_colon) = stripped.find(':') {
&value[second_colon + 2..]
} else {
value
}
} else {
value
};
result.push_str(&format!(":{tag}:{clean_value}\n"));
}
result
}
pub fn parse_sequences<T>(
fields: &HashMap<String, Vec<(String, usize)>>,
tracker: &mut FieldConsumptionTracker,
) -> Result<Vec<T>>
where
T: crate::SwiftMessageBody,
{
let message_type = std::any::type_name::<T>();
if message_type.contains("MT104Transaction") {
use crate::parser::sequence_parser::{get_sequence_config, split_into_sequences};
let config = get_sequence_config("MT104");
let parsed_sequences = split_into_sequences(fields, &config)?;
return parse_sequence_b_items::<T>(&parsed_sequences.sequence_b, tracker);
}
if message_type.contains("MT110Cheque") {
use crate::parser::sequence_parser::{get_sequence_config, split_into_sequences};
let config = get_sequence_config("MT110");
let parsed_sequences = split_into_sequences(fields, &config)?;
return parse_sequence_b_items::<T>(&parsed_sequences.sequence_b, tracker);
}
if message_type.contains("MT204Transaction") {
let field_20_count = fields.get("20").map(|v| v.len()).unwrap_or(0);
if field_20_count <= 1 {
return Ok(Vec::new()); }
let num_transactions = field_20_count - 1;
let mut transactions = Vec::new();
for i in 0..num_transactions {
let mut tx_fields = HashMap::new();
if let Some(field_20_values) = fields.get("20")
&& i + 1 < field_20_values.len()
{
tx_fields.insert("20".to_string(), vec![field_20_values[i + 1].clone()]);
}
if let Some(field_21_values) = fields.get("21")
&& i < field_21_values.len()
{
tx_fields.insert("21".to_string(), vec![field_21_values[i].clone()]);
}
if let Some(field_32b_values) = fields.get("32B")
&& i < field_32b_values.len()
{
tx_fields.insert("32B".to_string(), vec![field_32b_values[i].clone()]);
}
for variant in ["53", "53A", "53B", "53D"] {
if let Some(field_53_values) = fields.get(variant)
&& i < field_53_values.len()
{
tx_fields.insert(variant.to_string(), vec![field_53_values[i].clone()]);
break; }
}
if let Some(field_72_values) = fields.get("72") {
if i + 1 < field_72_values.len() {
tx_fields.insert("72".to_string(), vec![field_72_values[i + 1].clone()]);
}
}
let block4_str = reconstruct_block4_from_fields(&tx_fields);
if let Ok(transaction) = T::parse_from_block4(&block4_str) {
transactions.push(transaction);
}
}
return Ok(transactions);
}
let mut all_fields: Vec<(String, String, usize)> = Vec::new();
for (tag, values) in fields {
for (value, pos) in values {
if tracker
.consumed_indices
.get(tag)
.is_none_or(|set| !set.contains(pos))
{
#[cfg(debug_assertions)]
if tag.starts_with("50") {
eprintln!(
"DEBUG: Collecting field for sequence: tag='{}' from fields HashMap",
tag
);
}
all_fields.push((tag.clone(), value.clone(), *pos));
}
}
}
all_fields.sort_by_key(|(_, _, pos)| *pos);
let (primary_marker, secondary_marker) = if message_type.contains("MT920Sequence") {
("12", None)
} else if message_type.contains("MT935RateChange") {
("23", Some("25"))
} else if message_type.contains("MT940StatementLine")
|| message_type.contains("MT942StatementLine")
{
("61", None)
} else {
("21", None)
};
let mut sequences = Vec::new();
let mut current_sequence_fields: HashMap<String, Vec<(String, usize)>> = HashMap::new();
let mut in_sequence = false;
let is_mt942_statement = message_type.contains("MT942StatementLine");
let mut has_field_61_in_sequence = false;
let mut has_field_86_in_sequence = false;
for (tag, value, pos) in all_fields {
let is_sequence_start = (tag == primary_marker
|| secondary_marker.is_some_and(|m| tag == m))
&& !tag.ends_with("R")
&& !tag.ends_with("F")
&& !tag.ends_with("C")
&& !tag.ends_with("D");
if is_sequence_start {
if in_sequence && !current_sequence_fields.is_empty() {
let block4_str = reconstruct_block4_from_fields(¤t_sequence_fields);
if let Ok(sequence_item) = T::parse_from_block4(&block4_str) {
sequences.push(sequence_item);
}
current_sequence_fields.clear();
}
in_sequence = true;
has_field_61_in_sequence = true; has_field_86_in_sequence = false;
}
let should_include_in_sequence = if is_mt942_statement && in_sequence {
if tag == "61" {
if has_field_61_in_sequence && !current_sequence_fields.is_empty() {
let block4_str = reconstruct_block4_from_fields(¤t_sequence_fields);
if let Ok(sequence_item) = T::parse_from_block4(&block4_str) {
sequences.push(sequence_item);
}
current_sequence_fields.clear();
has_field_86_in_sequence = false;
}
has_field_61_in_sequence = true;
true
} else if tag == "86" && has_field_61_in_sequence && !has_field_86_in_sequence {
has_field_86_in_sequence = true;
true
} else {
false
}
} else if !is_mt942_statement && in_sequence {
true
} else {
false
};
if should_include_in_sequence {
#[cfg(debug_assertions)]
if tag.starts_with("50") || tag.starts_with("90") || tag.starts_with("86") {
eprintln!(
"DEBUG: Adding field to sequence: tag='{}', value_start='{}'",
tag,
value.lines().next().unwrap_or("")
);
}
current_sequence_fields
.entry(tag.clone())
.or_default()
.push((value, pos));
tracker.mark_consumed(&tag, pos);
} else if is_mt942_statement && in_sequence && !should_include_in_sequence {
if !current_sequence_fields.is_empty() {
let block4_str = reconstruct_block4_from_fields(¤t_sequence_fields);
if let Ok(sequence_item) = T::parse_from_block4(&block4_str) {
sequences.push(sequence_item);
}
current_sequence_fields.clear();
}
in_sequence = false;
has_field_61_in_sequence = false;
has_field_86_in_sequence = false;
#[cfg(debug_assertions)]
eprintln!("DEBUG: Ending MT942 sequence at field: tag='{}'", tag);
}
}
if in_sequence && !current_sequence_fields.is_empty() {
let block4_str = reconstruct_block4_from_fields(¤t_sequence_fields);
match T::parse_from_block4(&block4_str) {
Ok(sequence_item) => {
sequences.push(sequence_item);
}
Err(_e) => {
#[cfg(debug_assertions)]
eprintln!("DEBUG: Failed to parse final sequence item: {_e:?}");
}
}
}
Ok(sequences)
}
fn parse_sequence_b_items<T>(
fields: &HashMap<String, Vec<(String, usize)>>,
tracker: &mut FieldConsumptionTracker,
) -> Result<Vec<T>>
where
T: crate::SwiftMessageBody,
{
let mut sequences = Vec::new();
let mut all_fields: Vec<(String, String, usize)> = Vec::new();
for (tag, values) in fields {
for (value, pos) in values {
all_fields.push((tag.clone(), value.clone(), *pos));
}
}
all_fields.sort_by_key(|(_, _, pos)| *pos);
#[cfg(debug_assertions)]
eprintln!(
"DEBUG parse_sequence_b_items: found {} total fields to process",
all_fields.len()
);
let message_type = std::any::type_name::<T>();
let sequence_start_tag = if message_type.contains("MT204Transaction") {
"20" } else {
"21" };
let mut current_sequence_fields: HashMap<String, Vec<(String, usize)>> = HashMap::new();
let mut in_sequence = false;
for (tag, value, pos) in all_fields {
if tag == sequence_start_tag
&& !tag.ends_with("R")
&& !tag.ends_with("F")
&& !tag.ends_with("C")
&& !tag.ends_with("D")
{
#[cfg(debug_assertions)]
eprintln!(
"DEBUG: Found sequence start tag {} at position {}, in_sequence={}, current_fields_count={}",
tag,
pos,
in_sequence,
current_sequence_fields.len()
);
if in_sequence && !current_sequence_fields.is_empty() {
#[cfg(debug_assertions)]
eprintln!(
"DEBUG: Parsing previous sequence with {} field types",
current_sequence_fields.len()
);
let block4_str = reconstruct_block4_from_fields(¤t_sequence_fields);
match T::parse_from_block4(&block4_str) {
Ok(sequence_item) => {
sequences.push(sequence_item);
#[cfg(debug_assertions)]
eprintln!("DEBUG: Successfully parsed sequence #{}", sequences.len());
}
Err(e) => {
#[cfg(debug_assertions)]
eprintln!("DEBUG: Failed to parse sequence: {}", e);
}
}
current_sequence_fields.clear();
}
in_sequence = true;
}
if in_sequence {
#[cfg(debug_assertions)]
if tag.starts_with("50") {
eprintln!(
"DEBUG: Adding field to sequence: tag='{}', value_start='{}'",
tag,
value.lines().next().unwrap_or("")
);
}
current_sequence_fields
.entry(tag.clone())
.or_default()
.push((value, pos));
tracker.mark_consumed(&tag, pos);
}
}
if in_sequence && !current_sequence_fields.is_empty() {
#[cfg(debug_assertions)]
eprintln!(
"DEBUG: Parsing final sequence with {} field types",
current_sequence_fields.len()
);
let block4_str = reconstruct_block4_from_fields(¤t_sequence_fields);
match T::parse_from_block4(&block4_str) {
Ok(sequence_item) => {
sequences.push(sequence_item);
#[cfg(debug_assertions)]
eprintln!(
"DEBUG: Successfully parsed final sequence #{}",
sequences.len()
);
}
Err(_e) => {
#[cfg(debug_assertions)]
eprintln!("DEBUG: Failed to parse final sequence item: {_e:?}");
}
}
}
#[cfg(debug_assertions)]
eprintln!(
"DEBUG: parse_sequence_b_items returning {} sequences",
sequences.len()
);
Ok(sequences)
}