1use crate::error::Result;
16use crate::message::MessageBuilder;
17use crate::model::message::FixMessage;
18use crate::model::types::MsgType;
19use chrono::Utc;
20use deribit_base::impl_json_display;
21use serde::{Deserialize, Serialize};
22
23#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
29pub struct Heartbeat {
30 pub test_req_id: Option<String>,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
37pub enum BusinessRejectReason {
38 Other = 0,
40 UnknownId = 1,
42 UnknownSecurity = 2,
44 UnsupportedMessageType = 3,
46 ApplicationNotAvailable = 4,
48 ConditionallyRequiredFieldMissing = 5,
50 NotAuthorized = 6,
52 DeliverToFirmNotAvailableAtThisTime = 7,
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
61pub struct BusinessMessageReject {
62 pub ref_msg_type: String,
64
65 pub business_reject_reason: BusinessRejectReason,
67
68 pub business_reject_ref_id: Option<String>,
70
71 pub text: Option<String>,
73}
74
75#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
81pub struct TestRequest {
82 pub test_req_id: String,
85}
86
87#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
93pub struct ResendRequest {
94 pub begin_seq_no: u32,
96
97 pub end_seq_no: u32,
100}
101
102#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
108pub struct SequenceReset {
109 pub new_seq_no: u32,
112
113 pub gap_fill_flag: Option<bool>,
116}
117
118#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
124pub struct Reject {
125 pub ref_seq_num: u32,
127
128 pub ref_tag_id: Option<u32>,
131
132 pub ref_msg_type: Option<String>,
135
136 pub session_reject_reason: Option<u32>,
139
140 pub text: Option<String>,
143}
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
147pub enum SessionRejectReason {
148 InvalidTagNumber = 0,
150 RequiredTagMissing = 1,
152 TagNotDefinedForMessageType = 2,
154 UndefinedTag = 3,
156 TagSpecifiedWithoutValue = 4,
158 ValueIncorrectForTag = 5,
160 IncorrectDataFormat = 6,
162 DecryptionProblem = 7,
164 SignatureProblem = 8,
166 CompIdProblem = 9,
168 SendingTimeAccuracyProblem = 10,
170 InvalidMsgType = 11,
172 XmlValidationError = 12,
174 TagAppearsMoreThanOnce = 13,
176 TagSpecifiedOutOfOrder = 14,
178 RepeatingGroupFieldsOutOfOrder = 15,
180 IncorrectNumInGroupCount = 16,
182 NonDataValueIncludesFieldDelimiter = 17,
184 Other = 99,
186}
187
188impl_json_display!(Heartbeat);
190impl_json_display!(TestRequest);
191impl_json_display!(ResendRequest);
192impl_json_display!(SequenceReset);
193impl_json_display!(Reject);
194impl_json_display!(BusinessMessageReject);
195
196impl Heartbeat {
197 pub fn new() -> Self {
199 Self { test_req_id: None }
200 }
201
202 pub fn new_response(test_req_id: String) -> Self {
204 Self {
205 test_req_id: Some(test_req_id),
206 }
207 }
208
209 pub fn is_test_response(&self) -> bool {
211 self.test_req_id.is_some()
212 }
213
214 pub fn to_fix_message(
216 &self,
217 sender_comp_id: String,
218 target_comp_id: String,
219 msg_seq_num: u32,
220 ) -> Result<FixMessage> {
221 let mut builder = MessageBuilder::new()
222 .msg_type(MsgType::Heartbeat)
223 .sender_comp_id(sender_comp_id)
224 .target_comp_id(target_comp_id)
225 .msg_seq_num(msg_seq_num)
226 .sending_time(Utc::now());
227
228 if let Some(ref test_req_id) = self.test_req_id {
230 builder = builder.field(112, test_req_id.clone());
231 }
232
233 builder.build()
234 }
235}
236
237impl TestRequest {
238 pub fn new(test_req_id: String) -> Self {
240 Self { test_req_id }
241 }
242
243 pub fn new_with_timestamp() -> Self {
245 let test_req_id = format!("TESTREQ_{}", Utc::now().timestamp_millis());
246 Self::new(test_req_id)
247 }
248
249 pub fn to_fix_message(
251 &self,
252 sender_comp_id: String,
253 target_comp_id: String,
254 msg_seq_num: u32,
255 ) -> Result<FixMessage> {
256 MessageBuilder::new()
257 .msg_type(MsgType::TestRequest)
258 .sender_comp_id(sender_comp_id)
259 .target_comp_id(target_comp_id)
260 .msg_seq_num(msg_seq_num)
261 .sending_time(Utc::now())
262 .field(112, self.test_req_id.clone()) .build()
264 }
265}
266
267impl ResendRequest {
268 pub fn new(begin_seq_no: u32, end_seq_no: u32) -> Self {
270 Self {
271 begin_seq_no,
272 end_seq_no,
273 }
274 }
275
276 pub fn new_from_sequence(begin_seq_no: u32) -> Self {
278 Self {
279 begin_seq_no,
280 end_seq_no: 0, }
282 }
283
284 pub fn is_infinite_range(&self) -> bool {
286 self.end_seq_no == 0
287 }
288
289 pub fn message_count(&self) -> Option<u32> {
291 if self.is_infinite_range() {
292 None
293 } else {
294 Some(self.end_seq_no.saturating_sub(self.begin_seq_no) + 1)
295 }
296 }
297
298 pub fn to_fix_message(
300 &self,
301 sender_comp_id: String,
302 target_comp_id: String,
303 msg_seq_num: u32,
304 ) -> Result<FixMessage> {
305 MessageBuilder::new()
306 .msg_type(MsgType::ResendRequest)
307 .sender_comp_id(sender_comp_id)
308 .target_comp_id(target_comp_id)
309 .msg_seq_num(msg_seq_num)
310 .sending_time(Utc::now())
311 .field(7, self.begin_seq_no.to_string()) .field(16, self.end_seq_no.to_string()) .build()
314 }
315}
316
317impl SequenceReset {
318 pub fn new(new_seq_no: u32) -> Self {
320 Self {
321 new_seq_no,
322 gap_fill_flag: None,
323 }
324 }
325
326 pub fn new_gap_fill(new_seq_no: u32) -> Self {
328 Self {
329 new_seq_no,
330 gap_fill_flag: Some(true),
331 }
332 }
333
334 pub fn new_reset(new_seq_no: u32) -> Self {
336 Self {
337 new_seq_no,
338 gap_fill_flag: Some(false),
339 }
340 }
341
342 pub fn is_gap_fill(&self) -> bool {
344 self.gap_fill_flag.unwrap_or(false)
345 }
346
347 pub fn to_fix_message(
349 &self,
350 sender_comp_id: String,
351 target_comp_id: String,
352 msg_seq_num: u32,
353 ) -> Result<FixMessage> {
354 let mut builder = MessageBuilder::new()
355 .msg_type(MsgType::SequenceReset)
356 .sender_comp_id(sender_comp_id)
357 .target_comp_id(target_comp_id)
358 .msg_seq_num(msg_seq_num)
359 .sending_time(Utc::now())
360 .field(36, self.new_seq_no.to_string()); if let Some(gap_fill) = self.gap_fill_flag {
364 builder = builder.field(123, if gap_fill { "Y" } else { "N" }.to_string());
365 }
366
367 builder.build()
368 }
369}
370
371impl Reject {
372 pub fn new(ref_seq_num: u32) -> Self {
374 Self {
375 ref_seq_num,
376 ref_tag_id: None,
377 ref_msg_type: None,
378 session_reject_reason: None,
379 text: None,
380 }
381 }
382
383 pub fn new_detailed(
385 ref_seq_num: u32,
386 ref_tag_id: Option<u32>,
387 ref_msg_type: Option<String>,
388 session_reject_reason: Option<SessionRejectReason>,
389 text: Option<String>,
390 ) -> Self {
391 Self {
392 ref_seq_num,
393 ref_tag_id,
394 ref_msg_type,
395 session_reject_reason: session_reject_reason.map(|r| r as u32),
396 text,
397 }
398 }
399
400 pub fn new_invalid_tag(ref_seq_num: u32, tag_id: u32) -> Self {
402 Self::new_detailed(
403 ref_seq_num,
404 Some(tag_id),
405 None,
406 Some(SessionRejectReason::InvalidTagNumber),
407 Some(format!("Invalid tag number: {tag_id}")),
408 )
409 }
410
411 pub fn new_missing_tag(ref_seq_num: u32, tag_id: u32, msg_type: String) -> Self {
413 Self::new_detailed(
414 ref_seq_num,
415 Some(tag_id),
416 Some(msg_type),
417 Some(SessionRejectReason::RequiredTagMissing),
418 Some(format!("Required tag {tag_id} missing")),
419 )
420 }
421
422 pub fn new_incorrect_format(ref_seq_num: u32, tag_id: u32, text: String) -> Self {
424 Self::new_detailed(
425 ref_seq_num,
426 Some(tag_id),
427 None,
428 Some(SessionRejectReason::IncorrectDataFormat),
429 Some(text),
430 )
431 }
432
433 pub fn to_fix_message(
435 &self,
436 sender_comp_id: String,
437 target_comp_id: String,
438 msg_seq_num: u32,
439 ) -> Result<FixMessage> {
440 let mut builder = MessageBuilder::new()
441 .msg_type(MsgType::Reject)
442 .sender_comp_id(sender_comp_id)
443 .target_comp_id(target_comp_id)
444 .msg_seq_num(msg_seq_num)
445 .sending_time(Utc::now())
446 .field(45, self.ref_seq_num.to_string()); if let Some(ref_tag_id) = self.ref_tag_id {
450 builder = builder.field(371, ref_tag_id.to_string()); }
452
453 if let Some(ref ref_msg_type) = self.ref_msg_type {
454 builder = builder.field(372, ref_msg_type.clone()); }
456
457 if let Some(session_reject_reason) = self.session_reject_reason {
458 builder = builder.field(373, session_reject_reason.to_string()); }
460
461 if let Some(ref text) = self.text {
462 builder = builder.field(58, text.clone()); }
464
465 builder.build()
466 }
467}
468
469impl BusinessMessageReject {
470 pub fn new(ref_msg_type: String, business_reject_reason: BusinessRejectReason) -> Self {
472 Self {
473 ref_msg_type,
474 business_reject_reason,
475 business_reject_ref_id: None,
476 text: None,
477 }
478 }
479
480 pub fn with_ref_id(mut self, business_reject_ref_id: String) -> Self {
482 self.business_reject_ref_id = Some(business_reject_ref_id);
483 self
484 }
485
486 pub fn with_text(mut self, text: String) -> Self {
488 self.text = Some(text);
489 self
490 }
491
492 pub fn to_fix_message(
494 &self,
495 sender_comp_id: String,
496 target_comp_id: String,
497 msg_seq_num: u32,
498 ) -> Result<FixMessage> {
499 let mut builder = MessageBuilder::new()
500 .msg_type(MsgType::BusinessMessageReject)
501 .sender_comp_id(sender_comp_id)
502 .target_comp_id(target_comp_id)
503 .msg_seq_num(msg_seq_num)
504 .sending_time(Utc::now())
505 .field(372, self.ref_msg_type.clone()) .field(380, (self.business_reject_reason as u32).to_string()); if let Some(ref ref_id) = self.business_reject_ref_id {
509 builder = builder.field(379, ref_id.clone()); }
511
512 if let Some(ref text) = self.text {
513 builder = builder.field(58, text.clone()); }
515
516 builder.build()
517 }
518}
519
520impl Default for Heartbeat {
521 fn default() -> Self {
522 Self::new()
523 }
524}
525
526#[cfg(test)]
527mod tests {
528 use super::*;
529
530 #[test]
531 fn test_heartbeat_creation() {
532 let heartbeat = Heartbeat::new();
533 assert_eq!(heartbeat.test_req_id, None);
534 assert!(!heartbeat.is_test_response());
535
536 let response = Heartbeat::new_response("TEST123".to_string());
537 assert_eq!(response.test_req_id, Some("TEST123".to_string()));
538 assert!(response.is_test_response());
539 }
540
541 #[test]
542 fn test_test_request_creation() {
543 let test_req = TestRequest::new("REQ123".to_string());
544 assert_eq!(test_req.test_req_id, "REQ123");
545
546 let timestamp_req = TestRequest::new_with_timestamp();
547 assert!(timestamp_req.test_req_id.starts_with("TESTREQ_"));
548 }
549
550 #[test]
551 fn test_resend_request_creation() {
552 let resend = ResendRequest::new(10, 20);
553 assert_eq!(resend.begin_seq_no, 10);
554 assert_eq!(resend.end_seq_no, 20);
555 assert!(!resend.is_infinite_range());
556 assert_eq!(resend.message_count(), Some(11));
557
558 let infinite = ResendRequest::new_from_sequence(15);
559 assert_eq!(infinite.begin_seq_no, 15);
560 assert_eq!(infinite.end_seq_no, 0);
561 assert!(infinite.is_infinite_range());
562 assert_eq!(infinite.message_count(), None);
563 }
564
565 #[test]
566 fn test_reject_creation() {
567 let basic_reject = Reject::new(123);
568 assert_eq!(basic_reject.ref_seq_num, 123);
569 assert_eq!(basic_reject.ref_tag_id, None);
570
571 let invalid_tag = Reject::new_invalid_tag(456, 999);
572 assert_eq!(invalid_tag.ref_seq_num, 456);
573 assert_eq!(invalid_tag.ref_tag_id, Some(999));
574 assert_eq!(
575 invalid_tag.session_reject_reason,
576 Some(SessionRejectReason::InvalidTagNumber as u32)
577 );
578
579 let missing_tag = Reject::new_missing_tag(789, 35, "D".to_string());
580 assert_eq!(missing_tag.ref_seq_num, 789);
581 assert_eq!(missing_tag.ref_tag_id, Some(35));
582 assert_eq!(missing_tag.ref_msg_type, Some("D".to_string()));
583 assert_eq!(
584 missing_tag.session_reject_reason,
585 Some(SessionRejectReason::RequiredTagMissing as u32)
586 );
587 }
588
589 #[test]
590 fn test_session_reject_reason_values() {
591 assert_eq!(SessionRejectReason::InvalidTagNumber as u32, 0);
592 assert_eq!(SessionRejectReason::RequiredTagMissing as u32, 1);
593 assert_eq!(SessionRejectReason::Other as u32, 99);
594 }
595
596 #[test]
597 fn test_heartbeat_to_fix_message() {
598 let heartbeat = Heartbeat::new_response("TEST123".to_string());
599 let fix_msg = heartbeat.to_fix_message("SENDER".to_string(), "TARGET".to_string(), 100);
600
601 assert!(fix_msg.is_ok());
602 let msg = fix_msg.unwrap();
603 assert_eq!(msg.get_field(35), Some(&"0".to_string())); assert_eq!(msg.get_field(112), Some(&"TEST123".to_string())); }
606
607 #[test]
608 fn test_test_request_to_fix_message() {
609 let test_req = TestRequest::new("REQ456".to_string());
610 let fix_msg = test_req.to_fix_message("CLIENT".to_string(), "SERVER".to_string(), 200);
611
612 assert!(fix_msg.is_ok());
613 let msg = fix_msg.unwrap();
614 assert_eq!(msg.get_field(35), Some(&"1".to_string())); assert_eq!(msg.get_field(112), Some(&"REQ456".to_string())); }
617
618 #[test]
619 fn test_business_message_reject_to_fix_message() {
620 let bmr = BusinessMessageReject::new(
621 "D".to_string(),
622 BusinessRejectReason::UnsupportedMessageType,
623 )
624 .with_ref_id("ABC123".to_string())
625 .with_text("Unsupported type".to_string());
626
627 let fix_msg = bmr.to_fix_message("SENDER".to_string(), "TARGET".to_string(), 77);
628 assert!(fix_msg.is_ok());
629 let msg = fix_msg.unwrap();
630 assert_eq!(msg.get_field(35), Some(&"j".to_string())); assert_eq!(msg.get_field(372), Some(&"D".to_string())); assert_eq!(msg.get_field(379), Some(&"ABC123".to_string())); assert_eq!(
634 msg.get_field(380),
635 Some(&(BusinessRejectReason::UnsupportedMessageType as u32).to_string())
636 ); assert_eq!(msg.get_field(58), Some(&"Unsupported type".to_string())); }
639}