deribit_fix/model/
message.rs1use crate::DeribitFixError;
7use crate::model::types::MsgType;
8use std::str::FromStr;
9
10#[derive(Clone)]
12pub struct FixMessage {
13 pub fields: Vec<(u32, String)>,
15 pub raw_message: String,
17}
18
19impl FixMessage {
20 pub fn new() -> Self {
22 Self {
23 fields: Vec::new(),
24 raw_message: String::new(),
25 }
26 }
27
28 pub fn parse(raw_message: &str) -> crate::Result<Self> {
30 let mut fields = Vec::new();
31 for part in raw_message.split('\x01').filter(|s| !s.is_empty()) {
32 let mut pair = part.splitn(2, '=');
33 if let (Some(tag_str), Some(value)) = (pair.next(), pair.next()) {
34 if let Ok(tag) = tag_str.parse::<u32>() {
35 fields.push((tag, value.to_string()));
36 } else {
37 return Err(DeribitFixError::MessageParsing(format!(
38 "Invalid tag: {tag_str}"
39 )));
40 }
41 } else {
42 return Err(DeribitFixError::MessageParsing(format!(
43 "Invalid field: {part}"
44 )));
45 }
46 }
47
48 Ok(Self {
49 fields,
50 raw_message: raw_message.to_string(),
51 })
52 }
53
54 pub fn get_field(&self, tag: u32) -> Option<&String> {
56 self.fields.iter().find(|(t, _)| *t == tag).map(|(_, v)| v)
57 }
58
59 pub fn set_field(&mut self, tag: u32, value: String) {
61 if let Some(field) = self.fields.iter_mut().find(|(t, _)| *t == tag) {
62 field.1 = value;
63 } else {
64 self.fields.push((tag, value));
65 }
66 }
67
68 pub fn msg_type(&self) -> Option<MsgType> {
70 self.get_field(35).and_then(|s| s.parse().ok())
71 }
72
73 pub fn sender_comp_id(&self) -> Option<&String> {
75 self.get_field(49)
76 }
77
78 pub fn target_comp_id(&self) -> Option<&String> {
80 self.get_field(56)
81 }
82
83 pub fn msg_seq_num(&self) -> Option<u32> {
85 self.get_field(34)?.parse().ok()
86 }
87
88 pub fn has_field(&self, tag: u32) -> bool {
90 self.fields.iter().any(|(t, _)| *t == tag)
91 }
92
93 pub fn calculate_checksum(&self) -> u8 {
95 let mut field_pairs: Vec<_> = self.fields.iter().collect();
97 field_pairs.sort_by_key(|(tag, _)| *tag);
98
99 let mut message_parts = Vec::new();
100
101 for (tag, value) in field_pairs {
103 if *tag != 10 {
104 message_parts.push(format!("{tag}={value}"));
106 }
107 }
108
109 let message_str = message_parts.join("\x01") + "\x01";
110 let bytes = message_str.as_bytes();
111 let mut checksum: u32 = 0;
112
113 for &byte in bytes {
115 checksum += byte as u32;
116 }
117
118 (checksum % 256) as u8
119 }
120}
121
122impl FromStr for FixMessage {
123 type Err = DeribitFixError;
124
125 fn from_str(s: &str) -> Result<Self, Self::Err> {
126 Self::parse(s)
127 }
128}
129
130impl Default for FixMessage {
131 fn default() -> Self {
132 Self::new()
133 }
134}
135
136impl std::fmt::Display for FixMessage {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 write!(f, "{}", self.raw_message)
139 }
140}
141
142impl std::fmt::Debug for FixMessage {
143 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144 let mut readable_message = self.raw_message.replace('\x01', " | ");
146 if readable_message.ends_with(" | ") {
148 readable_message.truncate(readable_message.len() - 3);
149 }
150
151 let mut field_descriptions = Vec::new();
153 for (tag, value) in &self.fields {
154 let field_name = match *tag {
155 8 => "BeginString",
156 9 => "BodyLength",
157 35 => "MsgType",
158 49 => "SenderCompID",
159 56 => "TargetCompID",
160 34 => "MsgSeqNum",
161 52 => "SendingTime",
162 10 => "CheckSum",
163 11 => "ClOrdID",
164 37 => "OrderID",
165 38 => "OrderQty",
166 39 => "OrdStatus",
167 40 => "OrdType",
168 44 => "Price",
169 54 => "Side",
170 55 => "Symbol",
171 59 => "TimeInForce",
172 95 => "SecureDataLen",
173 96 => "SecureData",
174 98 => "EncryptMethod",
175 108 => "HeartBtInt",
176 553 => "Username",
177 554 => "Password",
178 584 => "MassStatusReqID",
179 585 => "MassStatusReqType",
180 710 => "PosReqID",
181 721 => "PosMaintRptID",
182 _ => "Unknown",
183 };
184 field_descriptions.push(format!("{field_name}({tag})={value}"));
185 }
186
187 f.debug_struct("FixMessage")
188 .field("fields", &field_descriptions)
189 .field("readable_message", &readable_message)
190 .finish()
191 }
192}