From 9e5aa999df9cb391aaac2e7bb243421ac723517f Mon Sep 17 00:00:00 2001 From: David Steiner Date: Tue, 9 Dec 2025 15:13:46 +0100 Subject: [PATCH 1/2] Replace fix44 references with dedicated transport only fields --- crates/hotfix-dictionary/Cargo.toml | 2 +- crates/hotfix-dictionary/src/dictionary.rs | 2 + crates/hotfix-dictionary/src/lib.rs | 5 ++ crates/hotfix-message/Cargo.toml | 4 +- crates/hotfix-message/build.rs | 5 +- crates/hotfix-message/src/encoding.rs | 5 +- .../src/encoding/definitions.rs | 8 +++ crates/hotfix-message/src/lib.rs | 2 + crates/hotfix-message/src/message.rs | 19 +++--- crates/hotfix-message/src/session_fields.rs | 6 ++ crates/hotfix/Cargo.toml | 5 +- crates/hotfix/src/lib.rs | 3 + crates/hotfix/src/message.rs | 10 +-- crates/hotfix/src/message/heartbeat.rs | 8 +-- crates/hotfix/src/message/logon.rs | 68 +++++++++++++++---- crates/hotfix/src/message/logout.rs | 8 +-- crates/hotfix/src/message/reject.rs | 63 +++++++++-------- crates/hotfix/src/message/resend_request.rs | 7 +- crates/hotfix/src/message/sequence_reset.rs | 15 ++-- crates/hotfix/src/message/test_request.rs | 8 +-- crates/hotfix/src/message/verification.rs | 15 ++-- crates/hotfix/src/message_utils.rs | 21 +++--- crates/hotfix/src/session.rs | 59 ++++++++-------- crates/hotfix/tests/store_tests.rs | 14 ++-- examples/load-testing/Cargo.toml | 2 +- examples/load-testing/src/main.rs | 4 +- examples/load-testing/src/messages.rs | 7 +- examples/simple-new-order/Cargo.toml | 2 +- examples/simple-new-order/src/main.rs | 2 +- examples/simple-new-order/src/messages.rs | 3 +- 30 files changed, 232 insertions(+), 150 deletions(-) create mode 100644 crates/hotfix-message/src/session_fields.rs diff --git a/crates/hotfix-dictionary/Cargo.toml b/crates/hotfix-dictionary/Cargo.toml index 4a6c5f1..e297a6a 100644 --- a/crates/hotfix-dictionary/Cargo.toml +++ b/crates/hotfix-dictionary/Cargo.toml @@ -17,7 +17,7 @@ fix40 = [] fix41 = [] fix42 = [] fix43 = [] -# FIX 4.4 is always enabled. +fix44 = [] fix50 = [] fix50sp1 = [] fix50sp2 = [] diff --git a/crates/hotfix-dictionary/src/dictionary.rs b/crates/hotfix-dictionary/src/dictionary.rs index 42b0008..933a413 100644 --- a/crates/hotfix-dictionary/src/dictionary.rs +++ b/crates/hotfix-dictionary/src/dictionary.rs @@ -110,6 +110,7 @@ impl Dictionary { } /// Creates a new [`Dictionary`] for FIX 4.4. + #[cfg(feature = "fix44")] pub fn fix44() -> Self { let spec = include_str!("resources/quickfix/FIX-4.4.xml"); Dictionary::from_quickfix_spec(spec).unwrap() @@ -160,6 +161,7 @@ impl Dictionary { Self::fix42(), #[cfg(feature = "fix43")] Self::fix43(), + #[cfg(feature = "fix44")] Self::fix44(), #[cfg(feature = "fix50")] Self::fix50(), diff --git a/crates/hotfix-dictionary/src/lib.rs b/crates/hotfix-dictionary/src/lib.rs index fb25d6d..83bce7b 100644 --- a/crates/hotfix-dictionary/src/lib.rs +++ b/crates/hotfix-dictionary/src/lib.rs @@ -30,10 +30,12 @@ pub type TagU32 = std::num::NonZeroU32; #[cfg(test)] mod test { use super::*; + #[cfg(feature = "fix44")] use crate::layout::LayoutItemKind; use std::collections::HashSet; #[test] + #[cfg(feature = "fix44")] fn fix44_quickfix_is_ok() { let dict = Dictionary::fix44(); let msg_heartbeat = dict.message_by_name("Heartbeat").unwrap(); @@ -77,6 +79,7 @@ mod test { } #[test] + #[cfg(feature = "fix44")] fn fix44_field_28_has_three_variants() { let dict = Dictionary::fix44(); let field_28 = dict.field_by_tag(28).unwrap(); @@ -85,6 +88,7 @@ mod test { } #[test] + #[cfg(feature = "fix44")] fn fix44_field_36_has_no_variants() { let dict = Dictionary::fix44(); let field_36 = dict.field_by_tag(36).unwrap(); @@ -93,6 +97,7 @@ mod test { } #[test] + #[cfg(feature = "fix44")] fn fix44_field_167_has_eucorp_variant() { let dict = Dictionary::fix44(); let field_167 = dict.field_by_tag(167).unwrap(); diff --git a/crates/hotfix-message/Cargo.toml b/crates/hotfix-message/Cargo.toml index d875faa..d5c23b1 100644 --- a/crates/hotfix-message/Cargo.toml +++ b/crates/hotfix-message/Cargo.toml @@ -13,6 +13,8 @@ categories.workspace = true [features] fix42 = ["hotfix-dictionary/fix42"] +fix44 = ["hotfix-dictionary/fix44"] + utils-chrono = [] [lints] @@ -37,7 +39,7 @@ quickcheck_macros.workspace = true [build-dependencies] hotfix-codegen = { version = "0.1.4", path = "../hotfix-codegen" } -hotfix-dictionary = { version = "0.1.3", path = "../hotfix-dictionary", features = ["fix42"] } +hotfix-dictionary = { version = "0.1.3", path = "../hotfix-dictionary", features = ["fixt11"] } chrono.workspace = true diff --git a/crates/hotfix-message/build.rs b/crates/hotfix-message/build.rs index b436eed..a58dc0c 100644 --- a/crates/hotfix-message/build.rs +++ b/crates/hotfix-message/build.rs @@ -9,11 +9,12 @@ use std::io::Write; use std::path::PathBuf; fn main() -> io::Result<()> { - // TODO: add other FIX versions #[cfg(feature = "fix42")] codegen(Dictionary::fix42(), "fix42.rs")?; - // FIX 4.4 is always enabled. + #[cfg(feature = "fix44")] codegen(Dictionary::fix44(), "fix44.rs")?; + + codegen(Dictionary::fixt11(), "fixt11.rs")?; Ok(()) } diff --git a/crates/hotfix-message/src/encoding.rs b/crates/hotfix-message/src/encoding.rs index 9f77a44..aa8f033 100644 --- a/crates/hotfix-message/src/encoding.rs +++ b/crates/hotfix-message/src/encoding.rs @@ -7,6 +7,9 @@ pub mod field_types; pub use buffer::{Buffer, BufferWriter}; pub use field_access::{FieldType, FieldValueError}; +pub use definitions::HardCodedFixFieldDefinition; #[cfg(feature = "fix42")] pub use definitions::fix42; -pub use definitions::{HardCodedFixFieldDefinition, fix44}; +#[cfg(feature = "fix44")] +pub use definitions::fix44; +pub use definitions::fixt11; diff --git a/crates/hotfix-message/src/encoding/definitions.rs b/crates/hotfix-message/src/encoding/definitions.rs index dfc4245..77fc0df 100644 --- a/crates/hotfix-message/src/encoding/definitions.rs +++ b/crates/hotfix-message/src/encoding/definitions.rs @@ -45,9 +45,17 @@ pub mod fix42 { include!(concat!(env!("OUT_DIR"), "/fix42.rs")); } +#[cfg(feature = "fix44")] #[allow(dead_code, unused, warnings, enum_variant_names)] #[rustfmt::skip] /// Field and message definitions for FIX.4.4. pub mod fix44 { include!(concat!(env!("OUT_DIR"), "/fix44.rs")); } + +#[allow(dead_code, unused, warnings, enum_variant_names)] +#[rustfmt::skip] +/// Field and message definitions for FIX.4.4. +pub mod fixt11 { + include!(concat!(env!("OUT_DIR"), "/fixt11.rs")); +} diff --git a/crates/hotfix-message/src/lib.rs b/crates/hotfix-message/src/lib.rs index 2082601..55e22ee 100644 --- a/crates/hotfix-message/src/lib.rs +++ b/crates/hotfix-message/src/lib.rs @@ -6,6 +6,7 @@ mod field_map; pub mod message; pub mod parsed_message; pub(crate) mod parts; +pub mod session_fields; mod tags; pub use builder::{MessageBuilder, SOH}; @@ -14,6 +15,7 @@ pub use encoding::field_access::FieldType; pub use encoding::field_types; #[cfg(feature = "fix42")] pub use encoding::fix42; +#[cfg(feature = "fix44")] pub use encoding::fix44; pub use encoding::{FieldValueError, HardCodedFixFieldDefinition}; pub use hotfix_derive::FieldType; diff --git a/crates/hotfix-message/src/message.rs b/crates/hotfix-message/src/message.rs index d947e53..254fb45 100644 --- a/crates/hotfix-message/src/message.rs +++ b/crates/hotfix-message/src/message.rs @@ -1,12 +1,13 @@ use std::io::Write; use crate::FieldType; +use crate::HardCodedFixFieldDefinition; use crate::builder::SOH; use crate::encoder::Encode; use crate::error::EncodingResult; use crate::field_map::{Field, FieldMap}; use crate::parts::{Body, Header, Part, RepeatingGroup, Trailer}; -use crate::{HardCodedFixFieldDefinition, fix44}; +use crate::session_fields::{BEGIN_STRING, BODY_LENGTH, CHECK_SUM, MSG_TYPE}; use hotfix_dictionary::{FieldLocation, IsFieldDefinition}; pub struct Message { @@ -22,8 +23,8 @@ impl Message { body: Body::default(), trailer: Trailer::default(), }; - msg.set(fix44::BEGIN_STRING, begin_string); - msg.set(fix44::MSG_TYPE, message_type); + msg.set(BEGIN_STRING, begin_string); + msg.set(MSG_TYPE, message_type); msg } @@ -39,18 +40,14 @@ impl Message { pub fn encode(&mut self, config: &Config) -> EncodingResult> { let mut buffer = Vec::new(); - self.trailer.pop(fix44::CHECK_SUM); + self.trailer.pop(CHECK_SUM); let body_length = self.header.calculate_length() + self.body.calculate_length() + self.trailer.calculate_length(); - self.set(fix44::BODY_LENGTH, format!("{body_length}").as_str()); + self.set(BODY_LENGTH, format!("{body_length}").as_str()); let check_sum_start = buffer.len(); - let starting_fields = vec![ - fix44::BEGIN_STRING.tag(), - fix44::BODY_LENGTH.tag(), - fix44::MSG_TYPE.tag(), - ]; + let starting_fields = vec![BEGIN_STRING.tag(), BODY_LENGTH.tag(), MSG_TYPE.tag()]; self.header .fields .write(config, &mut buffer, &starting_fields)?; @@ -61,7 +58,7 @@ impl Message { .iter() .fold(0u8, |acc, &x| acc.wrapping_add(x)); let checksum_value = format!("{checksum:03}"); - self.set(fix44::CHECK_SUM, checksum_value.as_str()); + self.set(CHECK_SUM, checksum_value.as_str()); buffer.write_all(b"10=")?; buffer.write_all(checksum_value.as_bytes())?; buffer.push(config.separator); diff --git a/crates/hotfix-message/src/session_fields.rs b/crates/hotfix-message/src/session_fields.rs new file mode 100644 index 0000000..37a1b6c --- /dev/null +++ b/crates/hotfix-message/src/session_fields.rs @@ -0,0 +1,6 @@ +//! Core fields for the session layer. +//! +//! These are shared between all versions of FIX. +//! We currently generate these from FIXT.1.1, +//! but they might be hard-coded in the future. +pub use crate::encoding::fixt11::*; diff --git a/crates/hotfix/Cargo.toml b/crates/hotfix/Cargo.toml index 11d940e..c501140 100644 --- a/crates/hotfix/Cargo.toml +++ b/crates/hotfix/Cargo.toml @@ -13,6 +13,7 @@ categories.workspace = true [features] default = ["test-utils"] +fix44 = ["hotfix-message/fix44"] redb = ["dep:redb"] mongodb = ["dep:mongodb"] test-utils = [] @@ -21,7 +22,7 @@ test-utils = [] workspace = true [dependencies] -hotfix-message = { version = "0.2.6", path = "../hotfix-message", features = ["utils-chrono"] } +hotfix-message = { version = "0.2.6", path = "../hotfix-message", default-features = false, features = ["utils-chrono"] } anyhow = { workspace = true } async-trait = { workspace = true } @@ -42,7 +43,7 @@ tracing = { workspace = true } uuid = { workspace = true, features = ["v4"] } [dev-dependencies] -hotfix-message = { version = "0.2.6", path = "../hotfix-message", features = ["utils-chrono"] } +hotfix-message = { version = "0.2.6", path = "../hotfix-message", features = ["fix44", "utils-chrono"] } testcontainers = { workspace = true } tokio = { workspace = true, features = ["test-util"] } diff --git a/crates/hotfix/src/lib.rs b/crates/hotfix/src/lib.rs index 851e943..169ab4d 100644 --- a/crates/hotfix/src/lib.rs +++ b/crates/hotfix/src/lib.rs @@ -34,3 +34,6 @@ pub mod transport; pub use application::Application; pub use hotfix_message::field_types; pub use hotfix_message::message::Message; + +#[cfg(feature = "fix44")] +pub use hotfix_message::fix44; diff --git a/crates/hotfix/src/message.rs b/crates/hotfix/src/message.rs index cad0560..f512c5e 100644 --- a/crates/hotfix/src/message.rs +++ b/crates/hotfix/src/message.rs @@ -1,8 +1,8 @@ //! FIX message abstractions to help with encoding and parsing of messages. use hotfix_message::error::EncodingError as EncodeError; pub use hotfix_message::field_types::Timestamp; -pub use hotfix_message::fix44; pub(crate) use hotfix_message::message::{Config, Message}; +use hotfix_message::session_fields::{MSG_SEQ_NUM, SENDER_COMP_ID, SENDING_TIME, TARGET_COMP_ID}; pub use hotfix_message::{Part, RepeatingGroup}; pub mod heartbeat; @@ -34,10 +34,10 @@ pub fn generate_message( message: impl FixMessage, ) -> Result, EncodeError> { let mut msg = Message::new(begin_string, message.message_type()); - msg.set(fix44::SENDER_COMP_ID, sender_comp_id); - msg.set(fix44::TARGET_COMP_ID, target_comp_id.as_bytes()); - msg.set(fix44::MSG_SEQ_NUM, msg_seq_num); - msg.set(fix44::SENDING_TIME, Timestamp::utc_now()); + msg.set(SENDER_COMP_ID, sender_comp_id); + msg.set(TARGET_COMP_ID, target_comp_id.as_bytes()); + msg.set(MSG_SEQ_NUM, msg_seq_num); + msg.set(SENDING_TIME, Timestamp::utc_now()); message.write(&mut msg); diff --git a/crates/hotfix/src/message/heartbeat.rs b/crates/hotfix/src/message/heartbeat.rs index b1d3f04..7c1ec22 100644 --- a/crates/hotfix/src/message/heartbeat.rs +++ b/crates/hotfix/src/message/heartbeat.rs @@ -1,7 +1,7 @@ -use hotfix_message::message::Message; -use hotfix_message::{Part, fix44}; - use crate::message::FixMessage; +use hotfix_message::Part; +use hotfix_message::message::Message; +use hotfix_message::session_fields::TEST_REQ_ID; #[derive(Clone, Debug, Default)] pub struct Heartbeat { @@ -19,7 +19,7 @@ impl Heartbeat { impl FixMessage for Heartbeat { fn write(&self, msg: &mut Message) { if let Some(req_id) = &self.test_req_id { - msg.set(fix44::TEST_REQ_ID, req_id.as_str()); + msg.set(TEST_REQ_ID, req_id.as_str()); } } diff --git a/crates/hotfix/src/message/logon.rs b/crates/hotfix/src/message/logon.rs index 86e4a9b..8f34609 100644 --- a/crates/hotfix/src/message/logon.rs +++ b/crates/hotfix/src/message/logon.rs @@ -1,13 +1,15 @@ -use hotfix_message::message::Message; -use hotfix_message::{Part, fix44}; - use crate::message::FixMessage; +use hotfix_message::message::Message; +use hotfix_message::session_fields::{ + ENCRYPT_METHOD, HEART_BT_INT, NEXT_EXPECTED_MSG_SEQ_NUM, RESET_SEQ_NUM_FLAG, +}; +use hotfix_message::{FieldType, Part}; #[derive(Clone, Debug)] pub struct Logon { - encrypt_method: fix44::EncryptMethod, + encrypt_method: EncryptMethod, heartbeat_interval: u64, - reset_seq_num_flag: fix44::ResetSeqNumFlag, + reset_seq_num_flag: ResetSeqNumFlag, next_expected_msg_seq_num: Option, } @@ -19,11 +21,11 @@ pub enum ResetSeqNumConfig { impl Logon { pub fn new(heartbeat_interval: u64, reset_config: ResetSeqNumConfig) -> Self { let (reset_seq_num_flag, next_expected_msg_seq_num) = match reset_config { - ResetSeqNumConfig::Reset => (fix44::ResetSeqNumFlag::Yes, None), - ResetSeqNumConfig::NoReset(next) => (fix44::ResetSeqNumFlag::No, next), + ResetSeqNumConfig::Reset => (ResetSeqNumFlag::Yes, None), + ResetSeqNumConfig::NoReset(next) => (ResetSeqNumFlag::No, next), }; Self { - encrypt_method: fix44::EncryptMethod::None, + encrypt_method: EncryptMethod::None, heartbeat_interval, reset_seq_num_flag, next_expected_msg_seq_num, @@ -33,12 +35,12 @@ impl Logon { impl FixMessage for Logon { fn write(&self, msg: &mut Message) { - msg.set(fix44::ENCRYPT_METHOD, self.encrypt_method); - msg.set(fix44::HEART_BT_INT, self.heartbeat_interval); - msg.set(fix44::RESET_SEQ_NUM_FLAG, self.reset_seq_num_flag); + msg.set(ENCRYPT_METHOD, self.encrypt_method); + msg.set(HEART_BT_INT, self.heartbeat_interval); + msg.set(RESET_SEQ_NUM_FLAG, self.reset_seq_num_flag); if let Some(next) = self.next_expected_msg_seq_num { - msg.set(fix44::NEXT_EXPECTED_MSG_SEQ_NUM, next); + msg.set(NEXT_EXPECTED_MSG_SEQ_NUM, next); } } @@ -50,3 +52,45 @@ impl FixMessage for Logon { todo!() } } + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FieldType)] +pub enum EncryptMethod { + /// Field variant '0'. + #[hotfix(variant = "0")] + None, + + /// Field variant '1'. + #[hotfix(variant = "1")] + Pkcs, + + /// Field variant '2'. + #[hotfix(variant = "2")] + Des, + + /// Field variant '3'. + #[hotfix(variant = "3")] + PkcsDes, + + /// Field variant '4'. + #[hotfix(variant = "4")] + PgpDes, + + /// Field variant '5'. + #[hotfix(variant = "5")] + PgpDesMd5, + + /// Field variant '6'. + #[hotfix(variant = "6")] + PemDesMd5, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FieldType)] +pub enum ResetSeqNumFlag { + /// Field variant 'Y'. + #[hotfix(variant = "Y")] + Yes, + + /// Field variant 'N'. + #[hotfix(variant = "N")] + No, +} diff --git a/crates/hotfix/src/message/logout.rs b/crates/hotfix/src/message/logout.rs index d85fa6f..4b4d55f 100644 --- a/crates/hotfix/src/message/logout.rs +++ b/crates/hotfix/src/message/logout.rs @@ -1,7 +1,7 @@ -use hotfix_message::message::Message; -use hotfix_message::{Part, fix44}; - use crate::message::FixMessage; +use hotfix_message::Part; +use hotfix_message::message::Message; +use hotfix_message::session_fields::TEXT; #[derive(Clone, Debug, Default)] pub struct Logout { @@ -17,7 +17,7 @@ impl Logout { impl FixMessage for Logout { fn write(&self, msg: &mut Message) { if let Some(value) = &self.text { - msg.set(fix44::TEXT, value.as_str()); + msg.set(TEXT, value.as_str()); } } diff --git a/crates/hotfix/src/message/reject.rs b/crates/hotfix/src/message/reject.rs index 48c2a1b..7eec3d6 100644 --- a/crates/hotfix/src/message/reject.rs +++ b/crates/hotfix/src/message/reject.rs @@ -1,7 +1,10 @@ use crate::message::FixMessage; -use hotfix_message::fix44::{MsgType, SessionRejectReason}; +use hotfix_message::Part; use hotfix_message::message::Message; -use hotfix_message::{Part, fix44}; +use hotfix_message::session_fields::{ + MsgType, REF_MSG_TYPE, REF_SEQ_NUM, REF_TAG_ID, SESSION_REJECT_REASON, SessionRejectReason, + TEXT, +}; #[derive(Clone, Debug)] pub(crate) struct Reject { @@ -51,19 +54,19 @@ impl Reject { impl FixMessage for Reject { fn write(&self, msg: &mut Message) { - msg.set(fix44::REF_SEQ_NUM, self.ref_seq_num); + msg.set(REF_SEQ_NUM, self.ref_seq_num); if let Some(ref_tag_id) = self.ref_tag_id { - msg.set(fix44::REF_TAG_ID, ref_tag_id); + msg.set(REF_TAG_ID, ref_tag_id); } if let Some(ref_msg_type) = self.ref_msg_type { - msg.set(fix44::REF_MSG_TYPE, ref_msg_type); + msg.set(REF_MSG_TYPE, ref_msg_type); } if let Some(session_reject_reason) = self.session_reject_reason { - msg.set(fix44::SESSION_REJECT_REASON, session_reject_reason); + msg.set(SESSION_REJECT_REASON, session_reject_reason); } if let Some(text) = &self.text { - msg.set(fix44::TEXT, text.as_str()); + msg.set(TEXT, text.as_str()); } } @@ -73,11 +76,11 @@ impl FixMessage for Reject { fn parse(message: &Message) -> Self { Self { - ref_seq_num: message.get(fix44::REF_SEQ_NUM).unwrap(), - ref_tag_id: message.get(fix44::REF_TAG_ID).ok(), - ref_msg_type: message.get(fix44::REF_MSG_TYPE).ok(), - session_reject_reason: message.get(fix44::SESSION_REJECT_REASON).ok(), - text: message.get::<&str>(fix44::TEXT).ok().map(|s| s.to_string()), + ref_seq_num: message.get(REF_SEQ_NUM).unwrap(), + ref_tag_id: message.get(REF_TAG_ID).ok(), + ref_msg_type: message.get(REF_MSG_TYPE).ok(), + session_reject_reason: message.get(SESSION_REJECT_REASON).ok(), + text: message.get::<&str>(TEXT).ok().map(|s| s.to_string()), } } } @@ -85,7 +88,6 @@ impl FixMessage for Reject { #[cfg(test)] mod tests { use super::*; - use hotfix_message::fix44::{MsgType, SessionRejectReason}; use hotfix_message::message::Message; #[test] @@ -95,14 +97,14 @@ mod tests { let mut msg = Message::new("FIX.4.4", "3"); reject.write(&mut msg); - assert_eq!(msg.get::(fix44::REF_SEQ_NUM).unwrap(), 123); - assert!(msg.get::(fix44::REF_TAG_ID).is_err()); - assert!(msg.get::(fix44::REF_MSG_TYPE).is_err()); + assert_eq!(msg.get::(REF_SEQ_NUM).unwrap(), 123); + assert!(msg.get::(REF_TAG_ID).is_err()); + assert!(msg.get::(REF_MSG_TYPE).is_err()); assert!( - msg.get::(fix44::SESSION_REJECT_REASON) + msg.get::(SESSION_REJECT_REASON) .is_err() ); - assert!(msg.get::<&str>(fix44::TEXT).is_err()); + assert!(msg.get::<&str>(TEXT).is_err()); } #[test] @@ -116,27 +118,24 @@ mod tests { let mut msg = Message::new("FIX.4.4", "3"); reject.write(&mut msg); - assert_eq!(msg.get::(fix44::REF_SEQ_NUM).unwrap(), 456); - assert_eq!(msg.get::(fix44::REF_TAG_ID).unwrap(), 35); + assert_eq!(msg.get::(REF_SEQ_NUM).unwrap(), 456); + assert_eq!(msg.get::(REF_TAG_ID).unwrap(), 35); assert_eq!( - msg.get::(fix44::REF_MSG_TYPE).unwrap(), + msg.get::(REF_MSG_TYPE).unwrap(), MsgType::ExecutionReport ); assert_eq!( - msg.get::(fix44::SESSION_REJECT_REASON) + msg.get::(SESSION_REJECT_REASON) .unwrap(), SessionRejectReason::InvalidTagNumber ); - assert_eq!( - msg.get::<&str>(fix44::TEXT).unwrap(), - "Invalid message format" - ); + assert_eq!(msg.get::<&str>(TEXT).unwrap(), "Invalid message format"); } #[test] fn test_parse_reject_with_required_fields_only() { let mut msg = Message::new("FIX.4.4", "3"); - msg.set(fix44::REF_SEQ_NUM, 999u64); + msg.set(REF_SEQ_NUM, 999u64); let parsed = Reject::parse(&msg); @@ -150,14 +149,14 @@ mod tests { #[test] fn test_parse_reject_with_all_fields() { let mut msg = Message::new("FIX.4.4", "3"); - msg.set(fix44::REF_SEQ_NUM, 777u64); - msg.set(fix44::REF_TAG_ID, 40u64); - msg.set(fix44::REF_MSG_TYPE, MsgType::OrderSingle); + msg.set(REF_SEQ_NUM, 777u64); + msg.set(REF_TAG_ID, 40u64); + msg.set(REF_MSG_TYPE, MsgType::OrderSingle); msg.set( - fix44::SESSION_REJECT_REASON, + SESSION_REJECT_REASON, SessionRejectReason::TagNotDefinedForThisMessageType, ); - msg.set(fix44::TEXT, "Field not allowed"); + msg.set(TEXT, "Field not allowed"); let parsed = Reject::parse(&msg); diff --git a/crates/hotfix/src/message/resend_request.rs b/crates/hotfix/src/message/resend_request.rs index 4818936..3eddc96 100644 --- a/crates/hotfix/src/message/resend_request.rs +++ b/crates/hotfix/src/message/resend_request.rs @@ -1,6 +1,7 @@ use crate::message::FixMessage; +use hotfix_message::Part; use hotfix_message::message::Message; -use hotfix_message::{Part, fix44}; +use hotfix_message::session_fields::{BEGIN_SEQ_NO, END_SEQ_NO}; #[derive(Clone, Copy)] pub struct ResendRequest { @@ -19,8 +20,8 @@ impl ResendRequest { impl FixMessage for ResendRequest { fn write(&self, msg: &mut Message) { - msg.set(fix44::BEGIN_SEQ_NO, self.begin_seq_no); - msg.set(fix44::END_SEQ_NO, self.end_seq_no); + msg.set(BEGIN_SEQ_NO, self.begin_seq_no); + msg.set(END_SEQ_NO, self.end_seq_no); } fn message_type(&self) -> &str { diff --git a/crates/hotfix/src/message/sequence_reset.rs b/crates/hotfix/src/message/sequence_reset.rs index b2f8417..2f4a5d4 100644 --- a/crates/hotfix/src/message/sequence_reset.rs +++ b/crates/hotfix/src/message/sequence_reset.rs @@ -1,7 +1,10 @@ use crate::message::FixMessage; +use hotfix_message::Part; use hotfix_message::field_types::Timestamp; use hotfix_message::message::Message; -use hotfix_message::{Part, fix44}; +use hotfix_message::session_fields::{ + GAP_FILL_FLAG, NEW_SEQ_NO, ORIG_SENDING_TIME, POSS_DUP_FLAG, SENDING_TIME, +}; #[derive(Clone, Copy)] pub struct SequenceReset { @@ -11,11 +14,11 @@ pub struct SequenceReset { impl FixMessage for SequenceReset { fn write(&self, msg: &mut Message) { - msg.set(fix44::GAP_FILL_FLAG, self.gap_fill); - msg.set(fix44::NEW_SEQ_NO, self.new_seq_no); - let sending_time: Timestamp = msg.header().get(fix44::SENDING_TIME).unwrap(); - msg.header_mut().set(fix44::ORIG_SENDING_TIME, sending_time); - msg.header_mut().set(fix44::POSS_DUP_FLAG, true); + msg.set(GAP_FILL_FLAG, self.gap_fill); + msg.set(NEW_SEQ_NO, self.new_seq_no); + let sending_time: Timestamp = msg.header().get(SENDING_TIME).unwrap(); + msg.header_mut().set(ORIG_SENDING_TIME, sending_time); + msg.header_mut().set(POSS_DUP_FLAG, true); } fn message_type(&self) -> &str { diff --git a/crates/hotfix/src/message/test_request.rs b/crates/hotfix/src/message/test_request.rs index e5931fc..afb2381 100644 --- a/crates/hotfix/src/message/test_request.rs +++ b/crates/hotfix/src/message/test_request.rs @@ -1,7 +1,7 @@ -use hotfix_message::message::Message; -use hotfix_message::{Part, fix44}; - use crate::message::FixMessage; +use hotfix_message::Part; +use hotfix_message::message::Message; +use hotfix_message::session_fields::TEST_REQ_ID; #[derive(Clone, Debug)] pub struct TestRequest { @@ -16,7 +16,7 @@ impl TestRequest { impl FixMessage for TestRequest { fn write(&self, msg: &mut Message) { - msg.set(fix44::TEST_REQ_ID, self.test_req_id.as_str()); + msg.set(TEST_REQ_ID, self.test_req_id.as_str()); } fn message_type(&self) -> &str { diff --git a/crates/hotfix/src/message/verification.rs b/crates/hotfix/src/message/verification.rs index 63eb591..9350b94 100644 --- a/crates/hotfix/src/message/verification.rs +++ b/crates/hotfix/src/message/verification.rs @@ -1,9 +1,12 @@ use crate::config::SessionConfig; use crate::error::{CompIdType, MessageVerificationError}; +use hotfix_message::Part; use hotfix_message::field_types::Timestamp; -use hotfix_message::fix44::{ORIG_SENDING_TIME, POSS_DUP_FLAG, SENDING_TIME}; use hotfix_message::message::Message; -use hotfix_message::{Part, fix44}; +use hotfix_message::session_fields::{ + BEGIN_STRING, MSG_SEQ_NUM, ORIG_SENDING_TIME, POSS_DUP_FLAG, SENDER_COMP_ID, SENDING_TIME, + TARGET_COMP_ID, +}; use std::cmp::Ordering; use tracing::error; @@ -16,7 +19,7 @@ pub(crate) fn verify_message( expected_seq_number: Option, ) -> Result<(), MessageVerificationError> { check_begin_string(message, config.begin_string.as_str())?; - let actual_seq_number: u64 = message.header().get(fix44::MSG_SEQ_NUM).unwrap_or_default(); + let actual_seq_number: u64 = message.header().get(MSG_SEQ_NUM).unwrap_or_default(); // our TargetCompId is always the same as the expected SenderCompId for them let expected_sender_comp_id: &str = config.target_comp_id.as_str(); @@ -44,7 +47,7 @@ fn check_begin_string( message: &Message, expected_begin_string: &str, ) -> Result<(), MessageVerificationError> { - let begin_string: &str = message.header().get(fix44::BEGIN_STRING).unwrap_or(""); + let begin_string: &str = message.header().get(BEGIN_STRING).unwrap_or(""); if begin_string != expected_begin_string { return Err(MessageVerificationError::IncorrectBeginString( begin_string.to_string(), @@ -122,7 +125,7 @@ fn check_sender_comp_id( sequence_number: u64, expected_comp_id: &str, ) -> Result<(), MessageVerificationError> { - let actual_sender_comp_id: &str = message.header().get(fix44::SENDER_COMP_ID).unwrap_or(""); + let actual_sender_comp_id: &str = message.header().get(SENDER_COMP_ID).unwrap_or(""); if actual_sender_comp_id != expected_comp_id { return Err(MessageVerificationError::IncorrectCompId { comp_id: actual_sender_comp_id.to_string(), @@ -163,7 +166,7 @@ fn check_target_comp_id( msg_seq_num: u64, expected_comp_id: &str, ) -> Result<(), MessageVerificationError> { - let actual_target_comp_id: &str = message.header().get(fix44::TARGET_COMP_ID).unwrap_or(""); + let actual_target_comp_id: &str = message.header().get(TARGET_COMP_ID).unwrap_or(""); if actual_target_comp_id != expected_comp_id { return Err(MessageVerificationError::IncorrectCompId { comp_id: actual_target_comp_id.to_string(), diff --git a/crates/hotfix/src/message_utils.rs b/crates/hotfix/src/message_utils.rs index ecc8f85..e4f6d88 100644 --- a/crates/hotfix/src/message_utils.rs +++ b/crates/hotfix/src/message_utils.rs @@ -4,9 +4,10 @@ pub fn is_admin(message_type: &str) -> bool { ADMIN_TYPES.contains(&message_type) } +use hotfix_message::Part; use hotfix_message::field_types::Timestamp; use hotfix_message::message::Message; -use hotfix_message::{Part, fix44}; +use hotfix_message::session_fields::{ORIG_SENDING_TIME, POSS_DUP_FLAG, SENDING_TIME}; /// Prepares a FIX message for resend per the FIX spec (PossDupFlag logic). /// @@ -20,17 +21,17 @@ use hotfix_message::{Part, fix44}; pub fn prepare_message_for_resend(msg: &mut Message) -> Result<(), &'static str> { let header = msg.header_mut(); - if header.get_raw(fix44::SENDING_TIME).is_none() { + if header.get_raw(SENDING_TIME).is_none() { return Err("Missing SendingTime (52)"); } - let already_poss_dup = header.get::(fix44::POSS_DUP_FLAG).unwrap_or(false); - let has_orig_sending_time = header.get_raw(fix44::ORIG_SENDING_TIME).is_some(); + let already_poss_dup = header.get::(POSS_DUP_FLAG).unwrap_or(false); + let has_orig_sending_time = header.get_raw(ORIG_SENDING_TIME).is_some(); if already_poss_dup && has_orig_sending_time { // Subsequent resend: refresh SendingTime only - return if header.pop(fix44::SENDING_TIME).is_some() { - header.set(fix44::SENDING_TIME, Timestamp::utc_now()); + return if header.pop(SENDING_TIME).is_some() { + header.set(SENDING_TIME, Timestamp::utc_now()); Ok(()) } else { Err("Failed to extract previous SendingTime") @@ -38,12 +39,12 @@ pub fn prepare_message_for_resend(msg: &mut Message) -> Result<(), &'static str> } // First resend path - if let Some(original_sending_time_field) = header.pop(fix44::SENDING_TIME) { + if let Some(original_sending_time_field) = header.pop(SENDING_TIME) { let original_ts = Timestamp::parse(&original_sending_time_field.data) .ok_or("Invalid original SendingTime format")?; - header.set(fix44::ORIG_SENDING_TIME, original_ts); - header.set(fix44::SENDING_TIME, Timestamp::utc_now()); - header.set(fix44::POSS_DUP_FLAG, true); + header.set(ORIG_SENDING_TIME, original_ts); + header.set(SENDING_TIME, Timestamp::utc_now()); + header.set(POSS_DUP_FLAG, true); Ok(()) } else { Err("Failed to extract original SendingTime") diff --git a/crates/hotfix/src/session.rs b/crates/hotfix/src/session.rs index eed7bc7..adce28f 100644 --- a/crates/hotfix/src/session.rs +++ b/crates/hotfix/src/session.rs @@ -17,7 +17,7 @@ use anyhow::{Result, anyhow}; use chrono::Utc; use hotfix_message::dict::Dictionary; use hotfix_message::message::{Config as MessageConfig, Message}; -use hotfix_message::{MessageBuilder, Part, fix44}; +use hotfix_message::{MessageBuilder, Part}; use std::pin::Pin; use tokio::select; use tokio::sync::mpsc; @@ -35,17 +35,19 @@ use crate::message::test_request::TestRequest; use crate::message::verification::verify_message; use crate::message_utils::{is_admin, prepare_message_for_resend}; use crate::session::admin_request::AdminRequest; +#[cfg(not(feature = "test-utils"))] +pub(crate) use crate::session::session_ref::InternalSessionRef; +#[cfg(feature = "test-utils")] +pub use crate::session::session_ref::InternalSessionRef; use crate::session::state::SessionState; use crate::session::state::{AwaitingResendTransitionOutcome, TestRequestId}; use crate::session_schedule::SessionSchedule; use event::SessionEvent; -use hotfix_message::fix44::SessionRejectReason; use hotfix_message::parsed_message::{InvalidReason, ParsedMessage}; - -#[cfg(not(feature = "test-utils"))] -pub(crate) use crate::session::session_ref::InternalSessionRef; -#[cfg(feature = "test-utils")] -pub use crate::session::session_ref::InternalSessionRef; +use hotfix_message::session_fields::{ + BEGIN_SEQ_NO, END_SEQ_NO, GAP_FILL_FLAG, MSG_SEQ_NUM, MSG_TYPE, NEW_SEQ_NO, + SessionRejectReason, TEST_REQ_ID, +}; pub use crate::session::info::{SessionInfo, Status}; pub use crate::session::session_handle::SessionHandle; @@ -92,6 +94,7 @@ impl, M: FixMessage, S: MessageStore> Session { fn get_data_dictionary(config: &SessionConfig) -> Dictionary { match &config.data_dictionary_path { None => match config.begin_string.as_str() { + #[cfg(feature = "fix44")] "FIX.4.4" => Dictionary::fix44(), _ => panic!("unsupported begin string: {}", config.begin_string), }, @@ -120,7 +123,7 @@ impl, M: FixMessage, S: MessageStore> Session { } ParsedMessage::Invalid { message, reason } => match reason { InvalidReason::InvalidField(tag) | InvalidReason::InvalidGroup(tag) => { - match message.header().get(fix44::MSG_SEQ_NUM) { + match message.header().get(MSG_SEQ_NUM) { Ok(msg_seq_num) => { let reject = Reject::new(msg_seq_num) .session_reject_reason(SessionRejectReason::InvalidTagNumber) @@ -140,7 +143,7 @@ impl, M: FixMessage, S: MessageStore> Session { self.handle_invalid_msg_type(message, &msg_type).await; } InvalidReason::InvalidOrderInGroup { tag, .. } => { - match message.header().get(fix44::MSG_SEQ_NUM) { + match message.header().get(MSG_SEQ_NUM) { Ok(msg_seq_num) => { let reject = Reject::new(msg_seq_num) .session_reject_reason( @@ -164,12 +167,12 @@ impl, M: FixMessage, S: MessageStore> Session { } async fn process_message(&mut self, message: Message) -> Result<()> { - let message_type = message.header().get(fix44::MSG_TYPE)?; + let message_type = message.header().get(MSG_TYPE)?; if let SessionState::AwaitingResend(state) = &mut self.state { let seq_number: u64 = message .header() - .get(fix44::MSG_SEQ_NUM) + .get(MSG_SEQ_NUM) .map_err(|e| anyhow!("failed to get seq number: {:?}", e))?; if seq_number > state.end_seq_number { state.inbound_queue.push_back(message); @@ -250,7 +253,7 @@ impl, M: FixMessage, S: MessageStore> Session { // process queued messages and resume normal operation debug!("resend is done, processing backlog"); while let Some(msg) = state.inbound_queue.pop_front() { - let seq_number: u64 = msg.get(fix44::MSG_SEQ_NUM).unwrap_or_else(|e| { + let seq_number: u64 = msg.get(MSG_SEQ_NUM).unwrap_or_else(|e| { error!("failed to get seq number: {:?}", e); 0 }); @@ -345,7 +348,7 @@ impl, M: FixMessage, S: MessageStore> Session { async fn on_heartbeat(&mut self, message: &Message) -> Result<()> { if let (Some(expected_req_id), Ok(message_req_id)) = ( &self.state.expected_test_response_id(), - message.get::<&str>(fix44::TEST_REQ_ID), + message.get::<&str>(TEST_REQ_ID), ) && expected_req_id.as_str() == message_req_id { debug!("received response for TestRequest, resetting timer"); @@ -356,7 +359,7 @@ impl, M: FixMessage, S: MessageStore> Session { } async fn on_test_request(&mut self, message: &Message) -> Result<()> { - let req_id: &str = message.get(fix44::TEST_REQ_ID).unwrap_or_else(|_| { + let req_id: &str = message.get(TEST_REQ_ID).unwrap_or_else(|_| { // TODO: send reject? todo!() }); @@ -374,13 +377,13 @@ impl, M: FixMessage, S: MessageStore> Session { warn!("received resend request while disconnected, ignoring"); } - let begin_seq_number: u64 = match message.get(fix44::BEGIN_SEQ_NO) { + let begin_seq_number: u64 = match message.get(BEGIN_SEQ_NO) { Ok(seq_number) => seq_number, Err(_) => { let reject = Reject::new( message .header() - .get(fix44::MSG_SEQ_NUM) + .get(MSG_SEQ_NUM) .map_err(|_| anyhow!("failed to get seq number"))?, ) .session_reject_reason(SessionRejectReason::RequiredTagMissing) @@ -390,7 +393,7 @@ impl, M: FixMessage, S: MessageStore> Session { } }; - let end_seq_number: u64 = match message.get(fix44::END_SEQ_NO) { + let end_seq_number: u64 = match message.get(END_SEQ_NO) { Ok(seq_number) => { let last_seq_number = self.store.next_sender_seq_number() - 1; if seq_number == 0 { @@ -403,7 +406,7 @@ impl, M: FixMessage, S: MessageStore> Session { let reject = Reject::new( message .header() - .get(fix44::MSG_SEQ_NUM) + .get(MSG_SEQ_NUM) .map_err(|_| anyhow!("failed to get seq number"))?, ) .session_reject_reason(SessionRejectReason::RequiredTagMissing) @@ -426,7 +429,7 @@ impl, M: FixMessage, S: MessageStore> Session { /// Returns whether the message should be processed as usual /// and whether the target sequence number should be incremented. async fn on_reject(&mut self, message: &Message) -> Result<()> { - if let Ok(seq_num) = message.get::(fix44::MSG_SEQ_NUM) + if let Ok(seq_num) = message.get::(MSG_SEQ_NUM) && seq_num == self.store.next_target_seq_number() { self.store.increment_target_seq_number().await?; @@ -438,15 +441,15 @@ impl, M: FixMessage, S: MessageStore> Session { async fn on_sequence_reset(&mut self, message: &Message) -> Result<()> { let msg_seq_num = message .header() - .get(fix44::MSG_SEQ_NUM) + .get(MSG_SEQ_NUM) .map_err(|_| anyhow!("failed to get seq number"))?; - let is_gap_fill: bool = message.get(fix44::GAP_FILL_FLAG).unwrap_or(false); + let is_gap_fill: bool = message.get(GAP_FILL_FLAG).unwrap_or(false); if let Err(err) = self.verify_message(message, is_gap_fill) { self.handle_verification_error(err).await; return Ok(()); } - let end: u64 = match message.get(fix44::NEW_SEQ_NO) { + let end: u64 = match message.get(NEW_SEQ_NO) { Ok(new_seq_no) => new_seq_no, Err(err) => { error!( @@ -607,7 +610,7 @@ impl, M: FixMessage, S: MessageStore> Session { } async fn handle_invalid_msg_type(&mut self, message: Message, msg_type: &str) { - match message.header().get(fix44::MSG_SEQ_NUM) { + match message.header().get(MSG_SEQ_NUM) { Ok(msg_seq_num) => { let reject = Reject::new(msg_seq_num) .session_reject_reason(SessionRejectReason::InvalidMsgtype) @@ -615,7 +618,7 @@ impl, M: FixMessage, S: MessageStore> Session { self.send_message(reject).await; #[allow(clippy::collapsible_if)] - if let Ok(seq_num) = message.header().get::(fix44::MSG_SEQ_NUM) + if let Ok(seq_num) = message.header().get::(MSG_SEQ_NUM) && self.store.next_target_seq_number() == seq_num { if let Err(err) = self.store.increment_target_seq_number().await { @@ -669,12 +672,8 @@ impl, M: FixMessage, S: MessageStore> Session { .build(msg.as_slice()) .into_message() .unwrap(); - sequence_number = message.header().get(fix44::MSG_SEQ_NUM).unwrap(); - let message_type: String = message - .header() - .get::<&str>(fix44::MSG_TYPE) - .unwrap() - .to_string(); + sequence_number = message.header().get(MSG_SEQ_NUM).unwrap(); + let message_type: String = message.header().get::<&str>(MSG_TYPE).unwrap().to_string(); if is_admin(message_type.as_str()) { if reset_start.is_none() { diff --git a/crates/hotfix/tests/store_tests.rs b/crates/hotfix/tests/store_tests.rs index 2228a60..a137c29 100644 --- a/crates/hotfix/tests/store_tests.rs +++ b/crates/hotfix/tests/store_tests.rs @@ -338,13 +338,13 @@ pub trait TestStoreFactory { } async fn create_test_store_factories() -> Vec> { - let mut stores: Vec> = Vec::new(); - - // Add in-memory store factory - stores.push(Box::new(InMemoryMessageStoreTestFactory {}) as Box); - - // Add file store factory - stores.push(Box::new(FileStoreTestFactory::new()) as Box); + #[allow(unused_mut)] + let mut stores: Vec> = vec![ + // Add in-memory store factory + Box::new(InMemoryMessageStoreTestFactory {}) as Box, + // Add file store factory + Box::new(FileStoreTestFactory::new()) as Box, + ]; // Add redb store factory if the feature is enabled #[cfg(feature = "redb")] diff --git a/examples/load-testing/Cargo.toml b/examples/load-testing/Cargo.toml index beb457d..6fdf5bc 100644 --- a/examples/load-testing/Cargo.toml +++ b/examples/load-testing/Cargo.toml @@ -7,7 +7,7 @@ license.workspace = true publish = false [dependencies] -hotfix = { path = "../../crates/hotfix", features = ["mongodb", "redb"] } +hotfix = { path = "../../crates/hotfix", features = ["fix44", "mongodb", "redb"] } async-trait.workspace = true clap = { workspace = true, features = ["derive"] } diff --git a/examples/load-testing/src/main.rs b/examples/load-testing/src/main.rs index 958a366..12c8b5f 100644 --- a/examples/load-testing/src/main.rs +++ b/examples/load-testing/src/main.rs @@ -6,9 +6,9 @@ use crate::messages::{ExecutionReport, Message, NewOrderSingle}; use clap::{Parser, ValueEnum}; use hotfix::config::SessionConfig; use hotfix::field_types::{Date, Timestamp}; +use hotfix::fix44; +use hotfix::fix44::OrdType; use hotfix::initiator::Initiator; -use hotfix::message::fix44; -use hotfix::message::fix44::OrdType; use hotfix::session::SessionHandle; use std::time::Instant; use tokio::runtime::Builder; diff --git a/examples/load-testing/src/messages.rs b/examples/load-testing/src/messages.rs index 88b76c2..d3f2feb 100644 --- a/examples/load-testing/src/messages.rs +++ b/examples/load-testing/src/messages.rs @@ -1,7 +1,8 @@ use hotfix::Message as HotfixMessage; use hotfix::field_types::{Date, Timestamp}; -use hotfix::message::fix44::{ORDER_ID, OrdStatus, OrdType, Side}; -use hotfix::message::{FixMessage, Part, RepeatingGroup, fix44}; +use hotfix::fix44; +use hotfix::fix44::{OrdStatus, OrdType, Side}; +use hotfix::message::{FixMessage, Part, RepeatingGroup}; #[derive(Debug, Clone)] #[allow(dead_code)] @@ -46,7 +47,7 @@ pub enum Message { impl Message { fn parse_execution_report_ack(message: &HotfixMessage) -> Self { let report = ExecutionReport { - order_id: message.get::<&str>(ORDER_ID).unwrap().to_string(), + order_id: message.get::<&str>(fix44::ORDER_ID).unwrap().to_string(), cl_ord_id: message.get::<&str>(fix44::CL_ORD_ID).unwrap().to_string(), exec_id: message.get::<&str>(fix44::EXEC_ID).unwrap().to_string(), exec_type: message.get::(fix44::EXEC_TYPE).unwrap(), diff --git a/examples/simple-new-order/Cargo.toml b/examples/simple-new-order/Cargo.toml index 5898c70..43ab193 100644 --- a/examples/simple-new-order/Cargo.toml +++ b/examples/simple-new-order/Cargo.toml @@ -7,7 +7,7 @@ license.workspace = true publish = false [dependencies] -hotfix = { path = "../../crates/hotfix", features = ["mongodb", "redb"] } +hotfix = { path = "../../crates/hotfix", features = ["fix44", "mongodb", "redb"] } hotfix-web = { path = "../../crates/hotfix-web", features = ["ui"] } async-trait.workspace = true diff --git a/examples/simple-new-order/src/main.rs b/examples/simple-new-order/src/main.rs index 1f4f679..bd782b5 100644 --- a/examples/simple-new-order/src/main.rs +++ b/examples/simple-new-order/src/main.rs @@ -6,8 +6,8 @@ use crate::messages::{Message, NewOrderSingle}; use clap::{Parser, ValueEnum}; use hotfix::config::Config; use hotfix::field_types::{Date, Timestamp}; +use hotfix::fix44; use hotfix::initiator::Initiator; -use hotfix::message::fix44; use hotfix::session::SessionHandle; use hotfix::store::mongodb::Client; use hotfix_web::{RouterConfig, build_router_with_config}; diff --git a/examples/simple-new-order/src/messages.rs b/examples/simple-new-order/src/messages.rs index aa852ea..3fa899d 100644 --- a/examples/simple-new-order/src/messages.rs +++ b/examples/simple-new-order/src/messages.rs @@ -1,6 +1,7 @@ use hotfix::Message as HotfixMessage; use hotfix::field_types::{Date, Timestamp}; -use hotfix::message::{FixMessage, Part, RepeatingGroup, fix44}; +use hotfix::fix44; +use hotfix::message::{FixMessage, Part, RepeatingGroup}; #[derive(Debug, Clone)] pub struct NewOrderSingle { From ee6af1b1e4caf8e4cf2a6ed46e277182000099d2 Mon Sep 17 00:00:00 2001 From: David Steiner Date: Tue, 9 Dec 2025 15:15:46 +0100 Subject: [PATCH 2/2] Add clippy without all targets and features to pipeline --- .github/workflows/ci.yml | 2 ++ crates/hotfix/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49018a9..a7bd067 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,8 @@ jobs: - name: Check formatting run: cargo fmt --check - name: Run clippy + run: cargo clippy -- -Dclippy::all -D warnings + - name: Run clippy (all targets and features) run: cargo clippy --all-targets --all-features -- -Dclippy::all -D warnings - name: Run tests run: cargo test --verbose diff --git a/crates/hotfix/Cargo.toml b/crates/hotfix/Cargo.toml index c501140..f14340b 100644 --- a/crates/hotfix/Cargo.toml +++ b/crates/hotfix/Cargo.toml @@ -22,7 +22,7 @@ test-utils = [] workspace = true [dependencies] -hotfix-message = { version = "0.2.6", path = "../hotfix-message", default-features = false, features = ["utils-chrono"] } +hotfix-message = { version = "0.2.6", path = "../hotfix-message", features = ["utils-chrono"] } anyhow = { workspace = true } async-trait = { workspace = true }