use crate::errors::{InvalidFieldFormatError, ParseError};
use crate::traits::SwiftField;
use std::collections::HashSet;
use super::field_extractor::extract_field_content;
#[derive(Debug)]
pub struct MessageParser<'a> {
input: &'a str,
position: usize,
fields_seen: HashSet<String>,
message_type: String,
allow_duplicates: bool,
}
impl<'a> MessageParser<'a> {
pub fn new(input: &'a str, message_type: &str) -> Self {
Self {
input,
position: 0,
fields_seen: HashSet::new(),
message_type: message_type.to_string(),
allow_duplicates: false,
}
}
pub fn with_duplicates(mut self, allow: bool) -> Self {
self.allow_duplicates = allow;
self
}
pub fn parse_field<T: SwiftField>(&mut self, tag: &str) -> Result<T, ParseError> {
let field_content = self.extract_field(tag, false)?;
T::parse(&field_content).map_err(|e| {
ParseError::InvalidFieldFormat(Box::new(InvalidFieldFormatError {
field_tag: tag.to_string(),
component_name: "field".to_string(),
value: field_content,
format_spec: "field format".to_string(),
position: Some(self.position),
inner_error: e.to_string(),
}))
})
}
pub fn parse_optional_field<T: SwiftField>(
&mut self,
tag: &str,
) -> Result<Option<T>, ParseError> {
if !self.detect_field(tag) {
return Ok(None);
}
match self.extract_field(tag, true) {
Ok(content) => {
let parsed = T::parse(&content).map_err(|e| {
ParseError::InvalidFieldFormat(Box::new(InvalidFieldFormatError {
field_tag: tag.to_string(),
component_name: "field".to_string(),
value: content,
format_spec: "field format".to_string(),
position: Some(self.position),
inner_error: e.to_string(),
}))
})?;
Ok(Some(parsed))
}
Err(_) => Ok(None), }
}
pub fn parse_repeated_field<T: SwiftField>(&mut self, tag: &str) -> Result<Vec<T>, ParseError> {
let mut results = Vec::new();
while let Ok(content) = self.extract_field(tag, true) {
let parsed = T::parse(&content).map_err(|e| {
ParseError::InvalidFieldFormat(Box::new(InvalidFieldFormatError {
field_tag: tag.to_string(),
component_name: "field".to_string(),
value: content,
format_spec: "field format".to_string(),
position: Some(self.position),
inner_error: e.to_string(),
}))
})?;
results.push(parsed);
}
Ok(results)
}
pub fn parse_variant_field<T: SwiftField>(&mut self, base_tag: &str) -> Result<T, ParseError> {
let variant = self.detect_variant(base_tag)?;
let full_tag = format!("{}{}", base_tag, variant);
let field_content = self.extract_field(&full_tag, false)?;
T::parse_with_variant(&field_content, Some(&variant), Some(base_tag)).map_err(|e| {
ParseError::InvalidFieldFormat(Box::new(InvalidFieldFormatError {
field_tag: full_tag,
component_name: "field".to_string(),
value: field_content,
format_spec: "field format".to_string(),
position: Some(self.position),
inner_error: e.to_string(),
}))
})
}
pub fn parse_optional_variant_field<T: SwiftField>(
&mut self,
base_tag: &str,
) -> Result<Option<T>, ParseError> {
match self.detect_variant_optional(base_tag) {
Some(variant) => {
let full_tag = format!("{}{}", base_tag, variant);
if let Ok(content) = self.extract_field(&full_tag, true) {
let parsed = T::parse_with_variant(&content, Some(&variant), Some(base_tag))
.map_err(|e| {
ParseError::InvalidFieldFormat(Box::new(InvalidFieldFormatError {
field_tag: full_tag,
component_name: "field".to_string(),
value: content,
format_spec: "field format".to_string(),
position: Some(self.position),
inner_error: e.to_string(),
}))
})?;
Ok(Some(parsed))
} else {
Ok(None)
}
}
None => Ok(None), }
}
fn extract_field(&mut self, tag: &str, optional: bool) -> Result<String, ParseError> {
if !self.allow_duplicates && self.fields_seen.contains(tag) && !optional {
return Err(ParseError::InvalidFormat {
message: format!("Duplicate field: {}", tag),
});
}
let extract_result = extract_field_content(&self.input[self.position..], tag);
match extract_result {
Some((content, consumed)) => {
self.position += consumed;
if !self.allow_duplicates {
self.fields_seen.insert(tag.to_string());
}
Ok(content)
}
None => {
if optional {
Err(ParseError::InvalidFormat {
message: format!("Optional field {} not found", tag),
})
} else {
Err(ParseError::MissingRequiredField {
field_tag: tag.to_string(),
field_name: tag.to_string(),
message_type: self.message_type.clone(),
position_in_block4: Some(self.position),
})
}
}
}
}
fn detect_variant(&self, base_tag: &str) -> Result<String, ParseError> {
let common_variants = vec!["A", "B", "C", "D", "E", "F", "G", "H", "K", "L"];
let remaining = &self.input[self.position..];
let trimmed = remaining.trim_start_matches(|c: char| c.is_whitespace());
for variant in common_variants {
let full_tag = format!("{}{}", base_tag, variant);
if trimmed.starts_with(&format!(":{}:", full_tag)) {
return Ok(variant.to_string());
}
}
if trimmed.starts_with(&format!(":{}:", base_tag)) {
return Ok(String::new());
}
Err(ParseError::MissingRequiredField {
field_tag: base_tag.to_string(),
field_name: base_tag.to_string(),
message_type: self.message_type.clone(),
position_in_block4: Some(self.position),
})
}
pub fn detect_variant_optional(&self, base_tag: &str) -> Option<String> {
let common_variants = vec!["A", "B", "C", "D", "E", "F", "G", "H", "K", "L"];
let remaining = &self.input[self.position..];
let trimmed = remaining.trim_start_matches(|c: char| c.is_whitespace());
for variant in common_variants {
let full_tag = format!("{}{}", base_tag, variant);
if trimmed.starts_with(&format!(":{}:", full_tag)) {
return Some(variant.to_string());
}
}
if trimmed.starts_with(&format!(":{}:", base_tag)) {
return Some(String::new());
}
None
}
pub fn position(&self) -> usize {
self.position
}
pub fn remaining(&self) -> &str {
&self.input[self.position..]
}
pub fn is_complete(&self) -> bool {
self.position >= self.input.len()
|| self.remaining().trim().is_empty()
|| self.remaining().trim() == "-"
}
pub fn detect_field(&self, tag: &str) -> bool {
let remaining = self.remaining();
let trimmed = remaining.trim_start_matches(|c: char| c.is_whitespace());
trimmed.starts_with(&format!(":{}:", tag))
}
pub fn peek_field_variant(&self, base_tag: &str) -> Option<String> {
let remaining = self.remaining();
let trimmed = remaining.trim_start_matches(|c: char| c.is_whitespace());
for variant in [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
] {
let search_pattern = format!(":{}{}:", base_tag, variant);
if trimmed.starts_with(&search_pattern) {
return Some(variant.to_string());
}
}
let search_pattern = format!(":{}:", base_tag);
if trimmed.starts_with(&search_pattern) {
return Some("".to_string()); }
None
}
}