Skip to content

Commit 7f9e084

Browse files
authored
feat: separate trait for outbound and inbound messages (#261)
* Introduce new traits for outbound and inbound message to eventually deprecate the FixMessage trait * Replace FixMessage with OutboundMessage in outbound messages channel * Constrain inbound messages to InboundMessage rather than FixMessage in the application * Remove references to FixMessage trait in admin messages * Replace references to FixMessage in tests * Replace references to FixMessage in session controller * Replace references to FixMessage in web wrapper * Replace uses of FixMessage in example apps * Completely deprecate and remove FixMessage trait * Split inbound and outbound messages in load testing app * Split up message types into outbound and inbound messages in simple new order example
1 parent f5d77ca commit 7f9e084

29 files changed

Lines changed: 171 additions & 181 deletions

crates/hotfix-web/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ mod session_controller;
55
use crate::endpoints::build_api_router;
66
use crate::session_controller::{HttpSessionController, SessionController};
77
use axum::Router;
8-
use hotfix::message::FixMessage;
8+
use hotfix::message::OutboundMessage;
99
use hotfix::session::SessionHandle;
1010

1111
#[derive(Clone)]
@@ -21,13 +21,13 @@ pub struct RouterConfig {
2121
}
2222

2323
/// Build a router with default configuration (admin endpoints disabled)
24-
pub fn build_router<M: FixMessage>(session_handle: SessionHandle<M>) -> Router {
24+
pub fn build_router<Outbound: OutboundMessage>(session_handle: SessionHandle<Outbound>) -> Router {
2525
build_router_with_config(session_handle, RouterConfig::default())
2626
}
2727

2828
/// Build a router with custom configuration
29-
pub fn build_router_with_config<M: FixMessage>(
30-
session_handle: SessionHandle<M>,
29+
pub fn build_router_with_config<Outbound: OutboundMessage>(
30+
session_handle: SessionHandle<Outbound>,
3131
config: RouterConfig,
3232
) -> Router {
3333
let controller = HttpSessionController { session_handle };

crates/hotfix-web/src/session_controller.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use hotfix::message::FixMessage;
1+
use hotfix::message::OutboundMessage;
22
use hotfix::session::{SessionHandle, SessionInfo};
33

44
/// Controller for session operations, providing both read access and administrative actions
@@ -11,12 +11,12 @@ pub trait SessionController: Clone + Send + Sync {
1111

1212
/// HTTP session controller implementation that wraps a SessionHandle
1313
#[derive(Clone)]
14-
pub struct HttpSessionController<M> {
15-
pub(crate) session_handle: SessionHandle<M>,
14+
pub struct HttpSessionController<Outbound> {
15+
pub(crate) session_handle: SessionHandle<Outbound>,
1616
}
1717

1818
#[async_trait::async_trait]
19-
impl<M: FixMessage> SessionController for HttpSessionController<M> {
19+
impl<Outbound: OutboundMessage> SessionController for HttpSessionController<Outbound> {
2020
async fn get_session_info(&self) -> anyhow::Result<SessionInfo> {
2121
self.session_handle.get_session_info().await
2222
}
@@ -34,7 +34,9 @@ impl<M: FixMessage> SessionController for HttpSessionController<M> {
3434
// Note: We can't use a blanket impl due to Rust's orphan rules (can't impl foreign trait for generic type)
3535
#[cfg(feature = "ui")]
3636
#[async_trait::async_trait]
37-
impl<M: FixMessage> hotfix_web_ui::SessionInfoProvider for HttpSessionController<M> {
37+
impl<Outbound: OutboundMessage> hotfix_web_ui::SessionInfoProvider
38+
for HttpSessionController<Outbound>
39+
{
3840
async fn get_session_info(&self) -> anyhow::Result<SessionInfo> {
3941
// Reuse the SessionController implementation
4042
SessionController::get_session_info(self).await
@@ -43,12 +45,12 @@ impl<M: FixMessage> hotfix_web_ui::SessionInfoProvider for HttpSessionController
4345

4446
// Allow extracting HttpSessionController from AppState for hotfix-web-ui
4547
#[cfg(feature = "ui")]
46-
impl<M> axum::extract::FromRef<crate::AppState<HttpSessionController<M>>>
47-
for HttpSessionController<M>
48+
impl<Outbound> axum::extract::FromRef<crate::AppState<HttpSessionController<Outbound>>>
49+
for HttpSessionController<Outbound>
4850
where
49-
M: FixMessage,
51+
Outbound: OutboundMessage,
5052
{
51-
fn from_ref(state: &crate::AppState<HttpSessionController<M>>) -> Self {
53+
fn from_ref(state: &crate::AppState<HttpSessionController<Outbound>>) -> Self {
5254
state.controller.clone()
5355
}
5456
}

crates/hotfix/src/application.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
#[async_trait::async_trait]
22
/// The application users of HotFIX can implement to hook into the engine.
3-
pub trait Application<M>: Send + Sync + 'static {
3+
pub trait Application<Inbound, Outbound>: Send + Sync + 'static {
44
/// Called when a message is sent to the engine to be sent to the counterparty.
55
///
66
/// This is invoked before the raw message is persisted in the message store.
7-
async fn on_outbound_message(&self, msg: &M) -> OutboundDecision;
7+
async fn on_outbound_message(&self, msg: &Outbound) -> OutboundDecision;
88
/// Called when a message is received from the counterparty.
99
///
1010
/// This is invoked after the message is verified and parsed into a typed message.
11-
async fn on_inbound_message(&self, msg: M) -> InboundDecision;
11+
async fn on_inbound_message(&self, msg: Inbound) -> InboundDecision;
1212
/// Called when the session is logged out.
1313
async fn on_logout(&mut self, reason: &str);
1414
/// Called when the session is logged on.

crates/hotfix/src/initiator.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,22 @@ use tracing::{debug, warn};
1313

1414
use crate::application::Application;
1515
use crate::config::SessionConfig;
16-
use crate::message::FixMessage;
16+
use crate::message::{InboundMessage, OutboundMessage};
1717
use crate::session::{InternalSessionRef, SessionHandle};
1818
use crate::store::MessageStore;
1919
use crate::transport::connect;
2020

2121
#[derive(Clone)]
22-
pub struct Initiator<M> {
22+
pub struct Initiator<Outbound> {
2323
pub config: SessionConfig,
24-
session_handle: SessionHandle<M>,
24+
session_handle: SessionHandle<Outbound>,
2525
completion_rx: watch::Receiver<bool>,
2626
}
2727

28-
impl<M: FixMessage> Initiator<M> {
29-
pub async fn start(
28+
impl<Outbound: OutboundMessage> Initiator<Outbound> {
29+
pub async fn start<Inbound: InboundMessage>(
3030
config: SessionConfig,
31-
application: impl Application<M>,
31+
application: impl Application<Inbound, Outbound>,
3232
store: impl MessageStore + Send + Sync + 'static,
3333
) -> Self {
3434
let session_ref = InternalSessionRef::new(config.clone(), application, store);
@@ -47,7 +47,7 @@ impl<M: FixMessage> Initiator<M> {
4747
}
4848
}
4949

50-
pub async fn send_message(&self, msg: M) -> anyhow::Result<()> {
50+
pub async fn send_message(&self, msg: Outbound) -> anyhow::Result<()> {
5151
self.session_handle.send_message(msg).await?;
5252

5353
Ok(())
@@ -57,7 +57,7 @@ impl<M: FixMessage> Initiator<M> {
5757
self.config.sender_comp_id == sender_comp_id && self.config.target_comp_id == target_comp_id
5858
}
5959

60-
pub fn session_handle(&self) -> SessionHandle<M> {
60+
pub fn session_handle(&self) -> SessionHandle<Outbound> {
6161
self.session_handle.clone()
6262
}
6363

@@ -85,9 +85,9 @@ impl<M: FixMessage> Initiator<M> {
8585
}
8686
}
8787

88-
async fn establish_connection<M: FixMessage>(
88+
async fn establish_connection<Outbound: OutboundMessage>(
8989
config: SessionConfig,
90-
session_ref: InternalSessionRef<M>,
90+
session_ref: InternalSessionRef<Outbound>,
9191
completion_tx: watch::Sender<bool>,
9292
) {
9393
loop {

crates/hotfix/src/message.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ pub mod verification;
1818
pub use parser::RawFixMessage;
1919
pub use resend_request::ResendRequest;
2020

21-
pub trait FixMessage: Clone + Send + 'static {
21+
pub trait OutboundMessage: Clone + Send + 'static {
2222
fn write(&self, msg: &mut Message);
2323

2424
fn message_type(&self) -> &str;
25+
}
2526

27+
pub trait InboundMessage: Clone + Send + 'static {
2628
fn parse(message: &Message) -> Self;
2729
}
2830

@@ -31,7 +33,7 @@ pub fn generate_message(
3133
sender_comp_id: &str,
3234
target_comp_id: &str,
3335
msg_seq_num: u64,
34-
message: impl FixMessage,
36+
message: impl OutboundMessage,
3537
) -> Result<Vec<u8>, EncodeError> {
3638
let mut msg = Message::new(begin_string, message.message_type());
3739
msg.set(SENDER_COMP_ID, sender_comp_id);
@@ -43,9 +45,3 @@ pub fn generate_message(
4345

4446
msg.encode(&Config::default())
4547
}
46-
47-
pub trait WriteMessage {
48-
fn write(&self, msg: &mut Message);
49-
50-
fn message_type(&self) -> &str;
51-
}

crates/hotfix/src/message/heartbeat.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::message::FixMessage;
1+
use crate::message::{InboundMessage, OutboundMessage};
22
use hotfix_message::Part;
33
use hotfix_message::message::Message;
44
use hotfix_message::session_fields::TEST_REQ_ID;
@@ -16,7 +16,7 @@ impl Heartbeat {
1616
}
1717
}
1818

19-
impl FixMessage for Heartbeat {
19+
impl OutboundMessage for Heartbeat {
2020
fn write(&self, msg: &mut Message) {
2121
if let Some(req_id) = &self.test_req_id {
2222
msg.set(TEST_REQ_ID, req_id.as_str());
@@ -26,7 +26,9 @@ impl FixMessage for Heartbeat {
2626
fn message_type(&self) -> &str {
2727
"0"
2828
}
29+
}
2930

31+
impl InboundMessage for Heartbeat {
3032
fn parse(_message: &Message) -> Self {
3133
// TODO: this needs to be implemented properly when we're implementing Test Requests
3234
Heartbeat { test_req_id: None }

crates/hotfix/src/message/logon.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::message::FixMessage;
1+
use crate::message::OutboundMessage;
22
use hotfix_message::message::Message;
33
use hotfix_message::session_fields::{
44
ENCRYPT_METHOD, HEART_BT_INT, NEXT_EXPECTED_MSG_SEQ_NUM, RESET_SEQ_NUM_FLAG,
@@ -33,7 +33,7 @@ impl Logon {
3333
}
3434
}
3535

36-
impl FixMessage for Logon {
36+
impl OutboundMessage for Logon {
3737
fn write(&self, msg: &mut Message) {
3838
msg.set(ENCRYPT_METHOD, self.encrypt_method);
3939
msg.set(HEART_BT_INT, self.heartbeat_interval);
@@ -47,10 +47,6 @@ impl FixMessage for Logon {
4747
fn message_type(&self) -> &str {
4848
"A"
4949
}
50-
51-
fn parse(_message: &Message) -> Self {
52-
todo!()
53-
}
5450
}
5551

5652
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FieldType)]

crates/hotfix/src/message/logout.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::message::FixMessage;
1+
use crate::message::OutboundMessage;
22
use hotfix_message::Part;
33
use hotfix_message::message::Message;
44
use hotfix_message::session_fields::TEXT;
@@ -14,7 +14,7 @@ impl Logout {
1414
}
1515
}
1616

17-
impl FixMessage for Logout {
17+
impl OutboundMessage for Logout {
1818
fn write(&self, msg: &mut Message) {
1919
if let Some(value) = &self.text {
2020
msg.set(TEXT, value.as_str());
@@ -24,8 +24,4 @@ impl FixMessage for Logout {
2424
fn message_type(&self) -> &str {
2525
"5"
2626
}
27-
28-
fn parse(_message: &Message) -> Self {
29-
unimplemented!()
30-
}
3127
}

crates/hotfix/src/message/reject.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::message::FixMessage;
1+
use crate::message::{InboundMessage, OutboundMessage};
22
use hotfix_message::Part;
33
use hotfix_message::message::Message;
44
use hotfix_message::session_fields::{
@@ -52,7 +52,7 @@ impl Reject {
5252
}
5353
}
5454

55-
impl FixMessage for Reject {
55+
impl OutboundMessage for Reject {
5656
fn write(&self, msg: &mut Message) {
5757
msg.set(REF_SEQ_NUM, self.ref_seq_num);
5858

@@ -73,7 +73,9 @@ impl FixMessage for Reject {
7373
fn message_type(&self) -> &str {
7474
"3"
7575
}
76+
}
7677

78+
impl InboundMessage for Reject {
7779
fn parse(message: &Message) -> Self {
7880
Self {
7981
ref_seq_num: message.get(REF_SEQ_NUM).unwrap(),

crates/hotfix/src/message/resend_request.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::message::FixMessage;
1+
use crate::message::OutboundMessage;
22
use hotfix_message::Part;
33
use hotfix_message::message::Message;
44
use hotfix_message::session_fields::{BEGIN_SEQ_NO, END_SEQ_NO};
@@ -18,7 +18,7 @@ impl ResendRequest {
1818
}
1919
}
2020

21-
impl FixMessage for ResendRequest {
21+
impl OutboundMessage for ResendRequest {
2222
fn write(&self, msg: &mut Message) {
2323
msg.set(BEGIN_SEQ_NO, self.begin_seq_no);
2424
msg.set(END_SEQ_NO, self.end_seq_no);
@@ -27,8 +27,4 @@ impl FixMessage for ResendRequest {
2727
fn message_type(&self) -> &str {
2828
"2"
2929
}
30-
31-
fn parse(_message: &Message) -> Self {
32-
todo!()
33-
}
3430
}

0 commit comments

Comments
 (0)