use crate::errors::Result;
use std::collections::HashMap;
pub type FieldMap = HashMap<String, Vec<(String, usize)>>;
#[derive(Debug, Clone)]
pub struct SequenceConfig {
pub sequence_b_marker: String,
pub sequence_c_fields: Vec<String>,
pub has_sequence_c: bool,
}
impl Default for SequenceConfig {
fn default() -> Self {
Self {
sequence_b_marker: "21".to_string(),
sequence_c_fields: vec![],
has_sequence_c: false,
}
}
}
#[derive(Debug)]
pub struct ParsedSequences {
pub sequence_a: FieldMap,
pub sequence_b: FieldMap,
pub sequence_c: FieldMap,
}
pub fn split_into_sequences(fields: &FieldMap, config: &SequenceConfig) -> Result<ParsedSequences> {
let mut seq_a = HashMap::new();
let mut seq_b = HashMap::new();
let mut seq_c = HashMap::new();
let mut all_fields: Vec<(&str, &(String, usize))> = Vec::new();
for (tag, values) in fields {
for value in values {
all_fields.push((tag.as_str(), value));
}
}
all_fields.sort_by_key(|(_, (_, pos))| *pos);
let mut first_b_marker_pos = None;
let mut _last_b_marker_pos = None;
let secondary_marker = if config.sequence_b_marker == "23" {
Some("25")
} else {
None
};
let sequence_a_fields = ["72", "77E", "79"];
for (tag, (_, pos)) in &all_fields {
if is_sequence_b_marker(tag, &config.sequence_b_marker)
|| (secondary_marker.is_some() && *tag == secondary_marker.unwrap())
{
if first_b_marker_pos.is_none() {
first_b_marker_pos = Some(*pos);
}
_last_b_marker_pos = Some(*pos);
}
}
let is_mt204 = config.sequence_b_marker == "20"
&& all_fields.iter().filter(|(tag, _)| *tag == "20").count() > 1;
let sequence_b_start_idx = if is_mt204 {
let mut found_first_20 = false;
all_fields.iter().position(|(tag, _)| {
if *tag == "20" {
if found_first_20 {
true } else {
found_first_20 = true;
false }
} else {
false
}
})
} else {
all_fields.iter().position(|(tag, _)| {
is_sequence_b_marker(tag, &config.sequence_b_marker)
|| (secondary_marker.is_some() && *tag == secondary_marker.unwrap())
})
};
let mut sequence_c_start_idx: Option<usize> = None;
if config.has_sequence_c && sequence_b_start_idx.is_some() {
if config.sequence_b_marker == "61" {
let seq_c_markers = config
.sequence_c_fields
.iter()
.filter(|f| *f != "86")
.collect::<Vec<_>>();
if let Some(seq_b_start) = sequence_b_start_idx {
for (i, (tag, _)) in all_fields.iter().enumerate().skip(seq_b_start) {
let base_tag = tag.trim_end_matches(char::is_alphabetic);
if seq_c_markers.iter().any(|marker| base_tag == *marker) {
sequence_c_start_idx = Some(i);
break;
}
}
}
} else {
let transaction_end_fields = ["59", "70", "71A", "77B", "36"];
let mut last_trans_end_idx: Option<usize> = None;
for (i, (tag, _)) in all_fields.iter().enumerate() {
let base_tag = tag.trim_end_matches(char::is_alphabetic);
if transaction_end_fields.contains(&base_tag) {
last_trans_end_idx = Some(i);
}
}
if let Some(last_end) = last_trans_end_idx {
for (i, (tag, _)) in all_fields.iter().enumerate().skip(last_end + 1) {
if config.sequence_c_fields.contains(&tag.to_string()) {
sequence_c_start_idx = Some(i);
break;
}
}
} else {
if let Some(seq_b_start) = sequence_b_start_idx {
for (i, (tag, _)) in all_fields.iter().enumerate().skip(seq_b_start) {
if config.sequence_c_fields.contains(&tag.to_string()) {
sequence_c_start_idx = Some(i);
break;
}
}
}
}
}
}
for (i, (tag, (value, pos))) in all_fields.iter().enumerate() {
if sequence_a_fields.contains(tag) {
seq_a
.entry(tag.to_string())
.or_insert_with(Vec::new)
.push((value.clone(), *pos));
continue;
}
if let Some(seq_b_start) = sequence_b_start_idx {
if i < seq_b_start {
seq_a
.entry(tag.to_string())
.or_insert_with(Vec::new)
.push((value.clone(), *pos));
} else if let Some(seq_c_start) = sequence_c_start_idx {
if i >= seq_c_start {
seq_c
.entry(tag.to_string())
.or_insert_with(Vec::new)
.push((value.clone(), *pos));
} else {
seq_b
.entry(tag.to_string())
.or_insert_with(Vec::new)
.push((value.clone(), *pos));
}
} else {
seq_b
.entry(tag.to_string())
.or_insert_with(Vec::new)
.push((value.clone(), *pos));
}
} else {
seq_a
.entry(tag.to_string())
.or_insert_with(Vec::new)
.push((value.clone(), *pos));
}
}
Ok(ParsedSequences {
sequence_a: seq_a,
sequence_b: seq_b,
sequence_c: seq_c,
})
}
pub fn parse_repetitive_sequence<T>(fields: &FieldMap, marker_field: &str) -> Result<Vec<FieldMap>>
where
T: crate::SwiftMessageBody,
{
let mut items = 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);
let mut current_item_fields: HashMap<String, Vec<(String, usize)>> = HashMap::new();
let mut in_item = false;
for (tag, value, pos) in all_fields {
if is_sequence_b_marker(&tag, marker_field) {
if in_item && !current_item_fields.is_empty() {
items.push(current_item_fields.clone());
current_item_fields.clear();
}
in_item = true;
}
if in_item {
current_item_fields
.entry(tag)
.or_default()
.push((value, pos));
}
}
if in_item && !current_item_fields.is_empty() {
items.push(current_item_fields);
}
Ok(items)
}
fn is_sequence_b_marker(tag: &str, marker: &str) -> bool {
if tag == marker {
return true;
}
if marker == "21" && tag == "21" {
return true;
}
false
}
pub fn get_sequence_config(message_type: &str) -> SequenceConfig {
match message_type {
"MT101" => SequenceConfig {
sequence_b_marker: "21".to_string(),
sequence_c_fields: vec![],
has_sequence_c: false,
},
"MT104" => SequenceConfig {
sequence_b_marker: "21".to_string(),
sequence_c_fields: vec![
"32B".to_string(),
"19".to_string(),
"71F".to_string(),
"71G".to_string(),
"53".to_string(),
],
has_sequence_c: true,
},
"MT107" => SequenceConfig {
sequence_b_marker: "21".to_string(),
sequence_c_fields: vec![],
has_sequence_c: false,
},
"MT110" => SequenceConfig {
sequence_b_marker: "21".to_string(),
sequence_c_fields: vec![],
has_sequence_c: false,
},
"MT204" => SequenceConfig {
sequence_b_marker: "20".to_string(),
sequence_c_fields: vec![],
has_sequence_c: false,
},
_ => SequenceConfig::default(),
}
}