use serde::{ser::SerializeMap, Serialize, Serializer};
use std::collections::{HashMap, HashSet, VecDeque};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn from_tag_value_to_json(input_message: &str) -> String {
match FixMessage::from_tag_value(input_message) {
Some(fix_message) => fix_message.to_json().to_string(),
None => String::from(r#"{"error": "Could not parse the given input. Is it a valid FIX message?"}"#)
}
}
#[cfg(feature = "debugging")]
macro_rules! debug {
($($arg:tt)*) => { println!($($arg)*); }
}
#[cfg(not(feature = "debugging"))]
macro_rules! debug {
($($arg:tt)*) => {};
}
#[derive(Debug, Clone)]
enum FixEntity {
Field(i32, String),
Group(FixGroup),
}
impl FixEntity {
fn get_tag(&self) -> i32 {
match self {
FixEntity::Field(tag, _dummy) => *tag,
FixEntity::Group(group) => group.no_tag,
}
}
fn get_field_value_i32(&self) -> i32 {
if let FixEntity::Field(_dummy, value) = self {
return value.parse().unwrap();
}
panic!("A Field was expected");
}
}
#[derive(Debug, Clone)]
struct FixComponent {
entities: Vec<FixEntity>,
}
impl FixComponent {
fn new(entities: Vec<FixEntity>) -> Self {
Self { entities }
}
}
impl Serialize for FixComponent {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(self.entities.len()))?;
for entity in &self.entities {
match entity {
FixEntity::Field(ref tag, ref value) => {
map.serialize_entry(tag, value)?;
}
FixEntity::Group(ref group) => {
map.serialize_entry(&group.no_tag, &group.instances)?;
}
}
}
map.end()
}
}
#[derive(Debug, Clone)]
struct FixGroup {
delimiter: i32, no_tag: i32, repetitions: i32,
current_iteration: i32,
known_tags: HashSet<i32>, instances: Vec<FixComponent>,
}
impl FixGroup {
fn new(delimiter: i32, index_first_delimiter: usize, component: &mut FixComponent) -> Self {
let group_instance =
FixComponent::new(component.entities.drain(index_first_delimiter..).collect());
let no_tag_field = component.entities.pop().unwrap();
Self {
no_tag: no_tag_field.get_tag(), delimiter,
repetitions: no_tag_field.get_field_value_i32(),
current_iteration: 1,
known_tags: Self::get_known_tags(&group_instance),
instances: vec![group_instance],
}
}
fn get_known_tags(group_instance: &FixComponent) -> HashSet<i32> {
let mut known_tags = HashSet::<i32>::new();
group_instance
.entities
.iter()
.for_each(|entity| match entity {
FixEntity::Field(tag, _value) => {
known_tags.insert(*tag);
}
FixEntity::Group(group) => {
group.known_tags.iter().for_each(|known_tag| {
known_tags.insert(*known_tag);
});
}
});
known_tags
}
fn create_new_instance(&mut self) {
self.instances.push(FixComponent::new(Vec::new()));
}
fn insert_known_tag(&mut self, tag: i32) {
self.known_tags.insert(tag);
}
}
#[derive(Debug)]
struct TagValue<'a>(i32, &'a str);
pub struct FixMessage {
root_component: FixComponent,
pending_tag_indices: HashMap<i32, VecDeque<usize>>,
candidate_indices: Vec<HashMap<i32, usize>>, active_groups: Vec<FixGroup>, }
impl FixMessage {
fn new() -> Self {
let mut candidate_indices = Vec::new();
candidate_indices.push(HashMap::new());
Self {
root_component: FixComponent::new(Vec::new()),
pending_tag_indices: HashMap::new(),
candidate_indices,
active_groups: Vec::new(),
}
}
pub fn from_tag_value(input_message: &str) -> Option<FixMessage> {
let tag_values = FixMessage::pre_process_message(&input_message)?;
let mut message = FixMessage::new();
for (index, tag_value) in tag_values.iter().enumerate() {
message
.pending_tag_indices
.entry(tag_value.0)
.or_insert_with(VecDeque::new)
.push_back(index);
}
message.check_message_is_valid();
for (index, tag_value) in tag_values.iter().enumerate() {
message.add_tag_value(tag_value.0, String::from(tag_value.1), index);
}
message.clean();
Some(message)
}
pub fn to_json(&self) -> serde_json::value::Value {
serde_json::json!(&self.root_component)
}
fn pre_process_message<'a>(input_message: &'a str) -> Option<Vec<TagValue<'a>>> {
const SHORTEST_MESSAGE_LENGTH: usize = 12; let input_message = &input_message[input_message.find("8=")?..];
if input_message.len() < SHORTEST_MESSAGE_LENGTH {
return None;
}
let mut end_of_message_found = false;
input_message
.split(&Self::get_separator(input_message)?)
.map(|tag_value| {
tag_value.split_at(tag_value.find('=').unwrap_or_else(|| tag_value.len()))
})
.inspect(|tag_value| {
if tag_value.1.is_empty() {
eprintln!("WARNING: Ignoring [{}]", tag_value.0);
} else if tag_value.1.len() == 1 {
eprintln!("WARNING: Tag {} has no value", tag_value.0);
}
})
.filter(|tag_value| !tag_value.1.is_empty())
.map(|tag_value| {
let tag = tag_value.0.parse().unwrap_or(0);
let value = &tag_value.1[1..];
if tag == 10 && value.len() > 3 {
debug!("Ignoring characters after checksum [{}]", &value[3..]);
return TagValue(tag, &value[0..3]);
}
if tag == 0 {
eprintln!("WARNING: Ignoring [{}={}]", tag_value.0, value);
}
TagValue(tag, value)
})
.take_while(|tag_value| {
if end_of_message_found {
eprintln!("WARNING: Detected tag after tag 10: {}", tag_value.0);
return false;
}
end_of_message_found = tag_value.0 == 10;
true
})
.filter(|tag_value| tag_value.0 != 0)
.map(Some)
.collect()
}
fn get_separator(fix_msg: &str) -> Option<String> {
let fix_version_re = regex::Regex::new(r"^8=FIXT?.\d{1}.\d{1}").unwrap();
let field_separator = &fix_msg[fix_version_re.shortest_match(fix_msg)?..]
.chars()
.take_while(|char| !char.is_digit(10))
.collect::<String>();
if field_separator == "" {
return None;
}
Some(field_separator.to_string())
}
fn check_message_is_valid(&self) {
if self.pending_tag_indices.get(&10).is_none() {
eprintln!("WARNING: Message is incomplete (missing tag 10)");
}
}
#[allow(unused_variables)]
fn add_tag_value(&mut self, tag: i32, value: String, index: usize) {
debug!(
"{}Index {} - Add {} - {}",
self.get_spaces(),
index,
tag,
value
);
self.remove_pending_tag(tag);
while self.is_parsing_group() && !self.tag_in_group(tag) {
self.close_group();
}
if self.repeated_candidate(tag) {
self.open_group(tag);
}
if self.is_parsing_group() {
self.set_known_tag_in_group(tag);
}
if self.is_new_iteration(tag) {
self.create_new_group_instance();
} else {
self.register_candidate(tag);
}
self.get_entities().push(FixEntity::Field(tag, value));
}
fn clean(&mut self) {
self.pending_tag_indices.clear();
self.candidate_indices.clear();
self.active_groups.clear();
}
fn open_group(&mut self, group_delimiter: i32) {
debug!("{}INFO: Group detected", self.get_spaces());
let group = FixGroup::new(
group_delimiter,
self.get_index_of_candidate(group_delimiter),
self.get_component(),
);
self.active_groups.push(group);
self.candidate_indices.push(HashMap::new());
}
fn get_candidates(&self) -> &HashMap<i32, usize> {
self.candidate_indices.last().unwrap()
}
fn get_candidates_mut(&mut self) -> &mut HashMap<i32, usize> {
self.candidate_indices.last_mut().unwrap()
}
fn get_index_of_candidate(&self, tag: i32) -> usize {
*self.get_candidates().get(&tag).unwrap()
}
fn register_candidate(&mut self, tag: i32) {
let candidate_index = self.get_entities().len();
self.get_candidates_mut().insert(tag, candidate_index);
}
fn repeated_candidate(&mut self, tag: i32) -> bool {
self.get_candidates().contains_key(&tag)
}
fn get_next_index_of_pending_tag(&self, tag: i32) -> Option<&usize> {
self.pending_tag_indices.get(&tag).unwrap().front()
}
fn remove_pending_tag(&mut self, tag: i32) {
self.pending_tag_indices.get_mut(&tag).unwrap().pop_front();
}
fn close_group(&mut self) {
debug!("{}INFO: Stop parsing group\n", self.get_spaces());
let closed_group = self.active_groups.pop().unwrap();
self.get_component()
.entities
.push(FixEntity::Group(closed_group));
self.candidate_indices.pop();
}
fn is_new_iteration(&self, tag: i32) -> bool {
self.is_parsing_group() && tag == self.active_group().delimiter
}
fn increment_iteration(&mut self) {
debug!(
"{}-- repetition {} --",
self.get_spaces(),
self.active_group().current_iteration + 1
);
self.active_group_mut().current_iteration += 1
}
fn get_entities(&mut self) -> &mut Vec<FixEntity> {
&mut self.get_component().entities
}
fn create_new_group_instance(&mut self) {
self.get_candidates_mut().clear();
self.increment_iteration();
self.active_group_mut().create_new_instance();
}
#[allow(dead_code)]
fn get_spaces(&self) -> String {
" ".repeat(self.active_groups.len() * 2)
}
fn set_known_tag_in_group(&mut self, tag: i32) {
self.active_group_mut().insert_known_tag(tag);
}
fn get_component(&mut self) -> &mut FixComponent {
if self.is_parsing_group() {
self.active_group_mut().instances.last_mut().unwrap()
} else {
&mut self.root_component
}
}
fn is_parsing_group(&self) -> bool {
!self.active_groups.is_empty()
}
fn active_group(&self) -> &FixGroup {
self.active_groups.last().unwrap()
}
fn active_group_mut(&mut self) -> &mut FixGroup {
self.active_groups.last_mut().unwrap()
}
fn tag_in_group(&mut self, tag: i32) -> bool {
if tag == 10 {
eprintln!("WARNING: End of message detected while parsing group");
return false;
}
!self.is_last_iteration()
|| self.is_known_group_tag(tag)
|| self.pending_tag_in_last_instance()
}
fn pending_tag_in_last_instance(&mut self) -> bool {
self.active_group().known_tags.iter().any(|known_tag| {
if let Some(tag_index) = self.get_next_index_of_pending_tag(*known_tag) {
return self.index_belongs_to_current_group(*tag_index);
}
false
})
}
fn index_belongs_to_current_group(&self, tag_index: usize) -> bool {
if let Some(delimiter_index) =
self.get_next_index_of_pending_tag(self.active_group().delimiter)
{
return tag_index < *delimiter_index;
}
true
}
fn is_known_group_tag(&self, tag: i32) -> bool {
self.active_group().known_tags.contains(&tag)
}
fn is_last_iteration(&self) -> bool {
self.active_group().current_iteration == self.active_group().repetitions
}
}