ironfix_engine/
application.rs1use async_trait::async_trait;
13use ironfix_core::message::{OwnedMessage, RawMessage};
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash)]
17pub struct SessionId {
18 pub begin_string: String,
20 pub sender_comp_id: String,
22 pub target_comp_id: String,
24 pub sender_sub_id: Option<String>,
26 pub target_sub_id: Option<String>,
28}
29
30impl SessionId {
31 #[must_use]
33 pub fn new(
34 begin_string: impl Into<String>,
35 sender_comp_id: impl Into<String>,
36 target_comp_id: impl Into<String>,
37 ) -> Self {
38 Self {
39 begin_string: begin_string.into(),
40 sender_comp_id: sender_comp_id.into(),
41 target_comp_id: target_comp_id.into(),
42 sender_sub_id: None,
43 target_sub_id: None,
44 }
45 }
46
47 #[must_use]
49 pub fn with_sender_sub_id(mut self, sub_id: impl Into<String>) -> Self {
50 self.sender_sub_id = Some(sub_id.into());
51 self
52 }
53
54 #[must_use]
56 pub fn with_target_sub_id(mut self, sub_id: impl Into<String>) -> Self {
57 self.target_sub_id = Some(sub_id.into());
58 self
59 }
60}
61
62impl std::fmt::Display for SessionId {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 write!(
65 f,
66 "{}:{}->{}",
67 self.begin_string, self.sender_comp_id, self.target_comp_id
68 )
69 }
70}
71
72#[derive(Debug, Clone)]
74pub struct RejectReason {
75 pub code: u32,
77 pub text: String,
79 pub ref_tag: Option<u32>,
81}
82
83impl RejectReason {
84 #[must_use]
86 pub fn new(code: u32, text: impl Into<String>) -> Self {
87 Self {
88 code,
89 text: text.into(),
90 ref_tag: None,
91 }
92 }
93
94 #[must_use]
96 pub const fn with_ref_tag(mut self, tag: u32) -> Self {
97 self.ref_tag = Some(tag);
98 self
99 }
100}
101
102#[async_trait]
107pub trait Application: Send + Sync {
108 async fn on_create(&self, session_id: &SessionId);
113
114 async fn on_logon(&self, session_id: &SessionId);
119
120 async fn on_logout(&self, session_id: &SessionId);
125
126 async fn to_admin(&self, message: &mut OwnedMessage, session_id: &SessionId);
134
135 #[allow(clippy::wrong_self_convention)]
144 async fn from_admin(
145 &self,
146 message: &RawMessage<'_>,
147 session_id: &SessionId,
148 ) -> Result<(), RejectReason>;
149
150 async fn to_app(&self, message: &mut OwnedMessage, session_id: &SessionId);
158
159 #[allow(clippy::wrong_self_convention)]
168 async fn from_app(
169 &self,
170 message: &RawMessage<'_>,
171 session_id: &SessionId,
172 ) -> Result<(), RejectReason>;
173}
174
175#[derive(Debug, Default)]
177pub struct NoOpApplication;
178
179#[async_trait]
180impl Application for NoOpApplication {
181 async fn on_create(&self, _session_id: &SessionId) {}
182
183 async fn on_logon(&self, _session_id: &SessionId) {}
184
185 async fn on_logout(&self, _session_id: &SessionId) {}
186
187 async fn to_admin(&self, _message: &mut OwnedMessage, _session_id: &SessionId) {}
188
189 async fn from_admin(
190 &self,
191 _message: &RawMessage<'_>,
192 _session_id: &SessionId,
193 ) -> Result<(), RejectReason> {
194 Ok(())
195 }
196
197 async fn to_app(&self, _message: &mut OwnedMessage, _session_id: &SessionId) {}
198
199 async fn from_app(
200 &self,
201 _message: &RawMessage<'_>,
202 _session_id: &SessionId,
203 ) -> Result<(), RejectReason> {
204 Ok(())
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn test_session_id() {
214 let id = SessionId::new("FIX.4.4", "SENDER", "TARGET");
215 assert_eq!(id.begin_string, "FIX.4.4");
216 assert_eq!(id.sender_comp_id, "SENDER");
217 assert_eq!(id.target_comp_id, "TARGET");
218 assert_eq!(id.to_string(), "FIX.4.4:SENDER->TARGET");
219 }
220
221 #[test]
222 fn test_reject_reason() {
223 let reason = RejectReason::new(1, "Invalid tag").with_ref_tag(35);
224 assert_eq!(reason.code, 1);
225 assert_eq!(reason.text, "Invalid tag");
226 assert_eq!(reason.ref_tag, Some(35));
227 }
228
229 #[tokio::test]
230 async fn test_noop_application() {
231 let app = NoOpApplication;
232 let session_id = SessionId::new("FIX.4.4", "SENDER", "TARGET");
233
234 app.on_create(&session_id).await;
235 app.on_logon(&session_id).await;
236 app.on_logout(&session_id).await;
237 }
238}