Skip to content

Commit

Permalink
feat(easyfix): separate deserializer from messages.
Browse files Browse the repository at this point in the history
  • Loading branch information
octaviandumitran committed Sep 24, 2024
1 parent c864fc9 commit 2836770
Show file tree
Hide file tree
Showing 12 changed files with 386 additions and 276 deletions.
2 changes: 2 additions & 0 deletions easyfix-dictionary/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ edition = "2021"
[dependencies]
anyhow = { workspace = true }
xmltree = "0.10"
strum = "0.26"
strum_macros = "0.26"
39 changes: 36 additions & 3 deletions easyfix-dictionary/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
#![feature(type_alias_impl_trait)]

use std::{collections::HashMap, convert::TryFrom, fmt, ops::Deref, str::FromStr};
use std::{
collections::HashMap,
convert::{AsRef, TryFrom},
fmt,
ops::Deref,
str::FromStr,
};

use anyhow::{anyhow, bail, Context as ErrorContext, Result};
use strum_macros::AsRefStr;
use xmltree::{Element, XMLNode};

type ElementIterator<'a> = impl Iterator<Item = &'a Element>;
Expand Down Expand Up @@ -523,16 +530,19 @@ pub struct Dictionary {
components_by_name: HashMap<String, Component>,
fields: HashMap<u16, Field>,
fields_by_name: HashMap<String, Field>,
reject_reason_overrides: HashMap<ParseRejectReason, String>,
}

impl Default for Dictionary {
fn default() -> Self {
Self::new()
Self::new(None)
}
}

impl Dictionary {
pub fn new() -> Dictionary {
pub fn new(
optional_reject_reason_overrides: Option<HashMap<ParseRejectReason, String>>,
) -> Dictionary {
Dictionary {
fixt_version: None,
fix_version: None,
Expand All @@ -544,6 +554,7 @@ impl Dictionary {
components_by_name: HashMap::new(),
fields: HashMap::new(),
fields_by_name: HashMap::new(),
reject_reason_overrides: optional_reject_reason_overrides.unwrap_or_default(),
}
}

Expand Down Expand Up @@ -724,6 +735,28 @@ impl Dictionary {
pub fn fields_by_name(&self) -> &HashMap<String, Field> {
&self.fields_by_name
}

pub fn reject_reason_overrides(&self) -> &HashMap<ParseRejectReason, String> {
&self.reject_reason_overrides
}
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, strum_macros::EnumIter, AsRefStr, Hash)]
pub enum ParseRejectReason {
ValueIsIncorrect,
TagSpecifiedWithoutAValue,
IncorrectDataFormatForValue,
TagAppearsMoreThanOnce,
TagSpecifiedOutOfRequiredOrder,
RequiredTagMissing,
IncorrectNumingroupCountForRepeatingGroup,
TagNotDefinedForThisMessageType,
UndefinedTag,
RepeatingGroupFieldsOutOfOrder,
InvalidTagNumber,
InvalidMsgtype,
SendingtimeAccuracyProblem,
CompidProblem,
}

#[cfg(test)]
Expand Down
1 change: 1 addition & 0 deletions easyfix-messages/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ fn main() {
out_path.join("generated_fields.rs"),
out_path.join("generated_groups.rs"),
out_path.join("generated_messages.rs"),
None,
)
.expect("failed to generate FIX messages");
}
3 changes: 3 additions & 0 deletions easyfix-messages/easyfix-messages-gen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ convert_case = "0.6"
easyfix-dictionary = { version = "0.3.2", path = "../../easyfix-dictionary" }
proc-macro2 = "1.0"
quote = "1.0"
strum = "0.26"
strum_macros = "0.26"

43 changes: 39 additions & 4 deletions easyfix-messages/easyfix-messages-gen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ mod structure;
use std::{collections::HashMap, rc::Rc};

use convert_case::{Case, Casing};
use easyfix_dictionary::{BasicType, Dictionary, Member, MemberKind};
use easyfix_dictionary::{BasicType, Dictionary, Member, MemberKind, ParseRejectReason};
use proc_macro2::{Ident, Literal, Span, TokenStream};
use quote::quote;
use strum::IntoEnumIterator;

use self::structure::MessageProperties;
use crate::gen::{
Expand All @@ -22,6 +23,7 @@ pub struct Generator {
enums: Vec<EnumDesc>,
fields_names: Vec<Ident>,
fields_numbers: Vec<u16>,
reject_reason_overrides: HashMap<ParseRejectReason, String>,
}

fn process_members(
Expand Down Expand Up @@ -256,6 +258,7 @@ impl Generator {
enums,
fields_names,
fields_numbers,
reject_reason_overrides: dictionary.reject_reason_overrides().clone(),
}
}

Expand All @@ -265,7 +268,31 @@ impl Generator {
enums.push(enum_.generate());
}

let mut reject_reason_map: HashMap<ParseRejectReason, String> = ParseRejectReason::iter()
.map(|reject_reason| (reject_reason, reject_reason.as_ref().to_string()))
.collect();
for (key, value) in self.reject_reason_overrides.clone() {
reject_reason_map.insert(key, value);
}

let reject_reason_vector: Vec<TokenStream> = reject_reason_map
.iter()
.map(|(key, value)| {
let parse_enum_name = Ident::new(key.as_ref(), Span::call_site());
let session_enum_name = Ident::new(value, Span::call_site());
quote ! { ParseRejectReason::#parse_enum_name => SessionRejectReason::#session_enum_name, }
})
.collect();

quote! {
use crate::deserializer::ParseRejectReason;

pub fn parse_reject_reason_to_session_reject_reason(input: ParseRejectReason) -> SessionRejectReason {
match input {
#(#reject_reason_vector)*
}
}

#(#enums)*
}
}
Expand All @@ -282,7 +309,7 @@ impl Generator {
quote! {
#[allow(unused_imports)]
use crate::{
deserializer::{DeserializeError, Deserializer},
deserializer::{DeserializeError, Deserializer, ParseRejectReason},
fields::{self, basic_types::*, SessionRejectReason},
serializer::Serializer,
};
Expand Down Expand Up @@ -332,7 +359,7 @@ impl Generator {
quote! {
#[allow(unused_imports)]
use crate::{
deserializer::{raw_message, DeserializeError, Deserializer, RawMessage},
deserializer::{raw_message, DeserializeError, Deserializer, RawMessage, ParseRejectReason},
fields::{self, basic_types::*, SessionRejectReason},
groups::*,
serializer::Serializer,
Expand Down Expand Up @@ -445,6 +472,13 @@ impl Generator {
serializer.take()
}

fn parse_msg_type(deserializer: &mut Deserializer<'_>, input: &FixStr) -> Result<MsgType, DeserializeError> {
let Ok(msg_type) = MsgType::try_from(input) else {
return Err(deserializer.reject(Some(35), ParseRejectReason::InvalidMsgtype));
};
Ok(msg_type)
}

pub fn deserialize(mut deserializer: Deserializer) -> Result<Box<FixtMessage>, DeserializeError> {
let begin_string = deserializer.begin_string();
if begin_string != BEGIN_STRING {
Expand All @@ -458,7 +492,8 @@ impl Generator {
.deserialize_tag_num()
.map_err(|e| DeserializeError::GarbledMessage(format!("failed to parse MsgType<35>: {}", e)))?
{
deserializer.deserialize_msg_type()?
let msg_type_string = deserializer.deserialize_msg_type()?;
Self::parse_msg_type(&mut deserializer, msg_type_string)?
} else {
return Err(DeserializeError::GarbledMessage("MsgType<35> not third tag".into()));
};
Expand Down
6 changes: 3 additions & 3 deletions easyfix-messages/easyfix-messages-gen/src/gen/enumeration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,12 @@ impl EnumDesc {
}

impl TryFrom<#type_> for #name {
type Error = SessionRejectReason;
type Error = ParseRejectReason;

fn try_from(input: #type_) -> Result<#name, SessionRejectReason> {
fn try_from(input: #type_) -> Result<#name, ParseRejectReason> {
#try_from_match_input {
#(#variant_value => Ok(#name::#variant_name),)*
_ => Err(SessionRejectReason::ValueIsIncorrect),
_ => Err(ParseRejectReason::ValueIsIncorrect),
}
}
}
Expand Down
26 changes: 13 additions & 13 deletions easyfix-messages/easyfix-messages-gen/src/gen/member.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,33 +378,33 @@ impl SimpleMember {
match (&self.type_, self.tag) {
// TODO: is it OK?
(_, 8 | 9 | 10 | 35) => Some(quote! {
return Err(deserializer.reject(Some(#tag), SessionRejectReason::TagAppearsMoreThanOnce));
return Err(deserializer.reject(Some(#tag), ParseRejectReason::TagAppearsMoreThanOnce));
}),
// MsgSeqNum
(_, 34) => Some(quote! {
if #name.is_some() {
return Err(deserializer.reject(Some(#tag), SessionRejectReason::TagAppearsMoreThanOnce));
return Err(deserializer.reject(Some(#tag), ParseRejectReason::TagAppearsMoreThanOnce));
}
let msg_seq_num_value = #deserialize?;
deserializer.set_seq_num(msg_seq_num_value);
#name = Some(msg_seq_num_value);
}),
(Type::Basic(BasicType::Length), _) => Some(quote! {
if #name.is_some() {
return Err(deserializer.reject(Some(#tag), SessionRejectReason::TagAppearsMoreThanOnce));
return Err(deserializer.reject(Some(#tag), ParseRejectReason::TagAppearsMoreThanOnce));
}
#name = Some(#deserialize?);
}),
(Type::Basic(BasicType::NumInGroup), _) => Some(quote! {
return Err(deserializer.reject(Some(tag), SessionRejectReason::TagSpecifiedOutOfRequiredOrder));
return Err(deserializer.reject(Some(tag), ParseRejectReason::TagSpecifiedOutOfRequiredOrder));
}),
(Type::Group(_), _) => None,
(Type::Basic(BasicType::Data | BasicType::XmlData), _) => Some(quote! {
return Err(deserializer.reject(Some(tag), SessionRejectReason::TagSpecifiedOutOfRequiredOrder));
return Err(deserializer.reject(Some(tag), ParseRejectReason::TagSpecifiedOutOfRequiredOrder));
}),
_ => Some(quote! {
if #name.is_some() {
return Err(deserializer.reject(Some(#tag), SessionRejectReason::TagAppearsMoreThanOnce));
return Err(deserializer.reject(Some(#tag), ParseRejectReason::TagAppearsMoreThanOnce));
}
#name = Some(#deserialize?);
}),
Expand All @@ -417,7 +417,7 @@ impl SimpleMember {
let tag = self.tag;
if self.required && !matches!(self.tag, 8 | 9 | 10 | 35) {
quote! {
#name: #name.ok_or_else(|| deserializer.reject(Some(#tag), SessionRejectReason::RequiredTagMissing))?
#name: #name.ok_or_else(|| deserializer.reject(Some(#tag), ParseRejectReason::RequiredTagMissing))?
}
} else {
quote! {
Expand Down Expand Up @@ -606,22 +606,22 @@ impl MemberDesc {
let next_member_deserialize = next_member_type.gen_deserialize();
Some(quote! {
if #name.is_some() {
return Err(deserializer.reject(Some(#tag), SessionRejectReason::TagAppearsMoreThanOnce));
return Err(deserializer.reject(Some(#tag), ParseRejectReason::TagAppearsMoreThanOnce));
}
// deserialize_data()/deserialize_xml() expects
// the name of variable below is `len`
let len = #deserialize?;
#name = Some(len);
if deserializer.deserialize_tag_num()?.ok_or_else(|| {
deserializer.reject(Some(#next_member_tag), SessionRejectReason::RequiredTagMissing)
deserializer.reject(Some(#next_member_tag), ParseRejectReason::RequiredTagMissing)
})? != #next_member_tag
{
return Err(deserializer.reject(Some(#tag), SessionRejectReason::TagSpecifiedOutOfRequiredOrder));
return Err(deserializer.reject(Some(#tag), ParseRejectReason::TagSpecifiedOutOfRequiredOrder));
}
// This should never happen, as error would be
// returned in #name.is_some() case.
if #next_member_name.is_some() {
return Err(deserializer.reject(Some(#tag), SessionRejectReason::TagAppearsMoreThanOnce));
return Err(deserializer.reject(Some(#tag), ParseRejectReason::TagAppearsMoreThanOnce));
}
#next_member_name = Some(#next_member_deserialize?);
})
Expand All @@ -645,12 +645,12 @@ impl MemberDesc {
Ident::new(&format!("{}_local", group_name), Span::call_site());
Some(quote! {
if #name.is_some() {
return Err(deserializer.reject(Some(#tag), SessionRejectReason::TagAppearsMoreThanOnce));
return Err(deserializer.reject(Some(#tag), ParseRejectReason::TagAppearsMoreThanOnce));
}
let len = #deserialize?;
#name = Some(len);
if #group_name.is_some() {
return Err(deserializer.reject(Some(#tag), SessionRejectReason::TagAppearsMoreThanOnce));
return Err(deserializer.reject(Some(#tag), ParseRejectReason::TagAppearsMoreThanOnce));
}
let num_in_group_tag = #tag;
let expected_tags = &[#(#expected_tags),*];
Expand Down
11 changes: 6 additions & 5 deletions easyfix-messages/easyfix-messages-gen/src/gen/structure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ impl Struct {
if tag == #first_member_tag && last_run || tag != #first_member_tag && !last_run {
return Err(deserializer.reject(
Some(num_in_group_tag),
SessionRejectReason::IncorrectNumingroupCountForRepeatingGroup,
ParseRejectReason::IncorrectNumingroupCountForRepeatingGroup,
));
}
deserializer.put_tag(tag);
Expand All @@ -123,7 +123,7 @@ impl Struct {
if tag == #first_member_tag && last_run || tag != #first_member_tag && !last_run {
return Err(deserializer.reject(
Some(num_in_group_tag),
SessionRejectReason::IncorrectNumingroupCountForRepeatingGroup,
ParseRejectReason::IncorrectNumingroupCountForRepeatingGroup,
));
} else {
deserializer.put_tag(tag);
Expand Down Expand Up @@ -167,7 +167,7 @@ impl Struct {
// defined as optional)
return Err(deserializer.reject(
Some(#first_member_tag),
SessionRejectReason::RequiredTagMissing)
ParseRejectReason::RequiredTagMissing)
)
};

Expand Down Expand Up @@ -235,9 +235,10 @@ impl Struct {
#(#de_match_entries,)*
tag => {
if FieldTag::from_tag_num(tag).is_some() {
return Err(deserializer.reject(Some(tag), SessionRejectReason::TagNotDefinedForThisMessageType));
return Err(deserializer.reject(Some(tag), ParseRejectReason::TagNotDefinedForThisMessageType));
} else {
return Err(deserializer.reject(Some(tag), SessionRejectReason::UndefinedTag));
return Err(deserializer.reject(Some(tag), ParseRejectReason::UndefinedTag
));
}
},
}
Expand Down
6 changes: 4 additions & 2 deletions easyfix-messages/easyfix-messages-gen/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod gen;

use std::{
collections::HashMap,
error::Error,
fs,
io::prelude::*,
Expand All @@ -9,7 +10,7 @@ use std::{
time::Instant,
};

use easyfix_dictionary::Dictionary;
use easyfix_dictionary::{Dictionary, ParseRejectReason};
use proc_macro2::TokenStream;

use crate::gen::Generator;
Expand Down Expand Up @@ -73,12 +74,13 @@ pub fn generate_fix_messages(
fields_file: impl AsRef<Path>,
groups_file: impl AsRef<Path>,
messages_file: impl AsRef<Path>,
reject_reason_overrides: Option<HashMap<ParseRejectReason, String>>,
) -> std::result::Result<(), Box<dyn std::error::Error + 'static>> {
eprintln!("fields file path: {}", fields_file.as_ref().display());
eprintln!("groups file path: {}", groups_file.as_ref().display());
eprintln!("messages file path: {}", messages_file.as_ref().display());
let fix_xml = fs::read_to_string(fix_xml_path)?;
let mut dictionary = Dictionary::new();
let mut dictionary = Dictionary::new(reject_reason_overrides);

if let Some(some_fixt_xml_path) = fixt_xml_path {
log_duration("FIXT XML processed", || {
Expand Down
Loading

0 comments on commit 2836770

Please sign in to comment.