Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion crates/hotfix-dictionary/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fix40 = []
fix41 = []
fix42 = []
fix43 = []
# FIX 4.4 is always enabled.
fix44 = []
fix50 = []
fix50sp1 = []
fix50sp2 = []
Expand Down
2 changes: 2 additions & 0 deletions crates/hotfix-dictionary/src/dictionary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -160,6 +161,7 @@ impl Dictionary {
Self::fix42(),
#[cfg(feature = "fix43")]
Self::fix43(),
#[cfg(feature = "fix44")]
Self::fix44(),
#[cfg(feature = "fix50")]
Self::fix50(),
Expand Down
5 changes: 5 additions & 0 deletions crates/hotfix-dictionary/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand Down
4 changes: 3 additions & 1 deletion crates/hotfix-message/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ categories.workspace = true

[features]
fix42 = ["hotfix-dictionary/fix42"]
fix44 = ["hotfix-dictionary/fix44"]

utils-chrono = []

[lints]
Expand All @@ -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

Expand Down
5 changes: 3 additions & 2 deletions crates/hotfix-message/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}

Expand Down
5 changes: 4 additions & 1 deletion crates/hotfix-message/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
8 changes: 8 additions & 0 deletions crates/hotfix-message/src/encoding/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
}
2 changes: 2 additions & 0 deletions crates/hotfix-message/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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;
Expand Down
19 changes: 8 additions & 11 deletions crates/hotfix-message/src/message.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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
}
Expand All @@ -39,18 +40,14 @@ impl Message {
pub fn encode(&mut self, config: &Config) -> EncodingResult<Vec<u8>> {
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)?;
Expand All @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions crates/hotfix-message/src/session_fields.rs
Original file line number Diff line number Diff line change
@@ -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::*;
3 changes: 2 additions & 1 deletion crates/hotfix/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ categories.workspace = true

[features]
default = ["test-utils"]
fix44 = ["hotfix-message/fix44"]
redb = ["dep:redb"]
mongodb = ["dep:mongodb"]
test-utils = []
Expand Down Expand Up @@ -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"] }
3 changes: 3 additions & 0 deletions crates/hotfix/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
10 changes: 5 additions & 5 deletions crates/hotfix/src/message.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -34,10 +34,10 @@ pub fn generate_message(
message: impl FixMessage,
) -> Result<Vec<u8>, 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);

Expand Down
8 changes: 4 additions & 4 deletions crates/hotfix/src/message/heartbeat.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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());
}
}

Expand Down
68 changes: 56 additions & 12 deletions crates/hotfix/src/message/logon.rs
Original file line number Diff line number Diff line change
@@ -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<u64>,
}

Expand All @@ -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,
Expand All @@ -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);
}
}

Expand All @@ -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,
}
Loading