deribit_fix/message/
mod.rs

1//! FIX message parsing and construction
2
3use crate::{
4    error::{DeribitFixError, Result},
5    types::MsgType,
6};
7use chrono::{DateTime, Utc};
8use std::collections::HashMap;
9use tracing::{debug, warn};
10
11/// FIX message representation
12#[derive(Debug, Clone)]
13pub struct FixMessage {
14    pub fields: HashMap<u32, String>,
15    pub raw_message: String,
16}
17
18impl FixMessage {
19    /// Create a new empty FIX message
20    pub fn new() -> Self {
21        Self {
22            fields: HashMap::new(),
23            raw_message: String::new(),
24        }
25    }
26
27    /// Parse a FIX message from a string
28    pub fn parse(raw_message: &str) -> Result<Self> {
29        let mut fields = HashMap::new();
30        
31        // Split by SOH character (ASCII 1)
32        let parts: Vec<&str> = raw_message.split('\x01').collect();
33        
34        for part in parts {
35            if part.is_empty() {
36                continue;
37            }
38            
39            if let Some(eq_pos) = part.find('=') {
40                let tag_str = &part[..eq_pos];
41                let value = &part[eq_pos + 1..];
42                
43                if let Ok(tag) = tag_str.parse::<u32>() {
44                    fields.insert(tag, value.to_string());
45                } else {
46                    warn!("Invalid FIX tag: {}", tag_str);
47                }
48            }
49        }
50
51        // Validate required fields
52        if !fields.contains_key(&8) {  // BeginString
53            return Err(DeribitFixError::MessageParsing("Missing BeginString (8)".to_string()));
54        }
55        
56        if !fields.contains_key(&35) { // MsgType
57            return Err(DeribitFixError::MessageParsing("Missing MsgType (35)".to_string()));
58        }
59
60        Ok(Self {
61            fields,
62            raw_message: raw_message.to_string(),
63        })
64    }
65
66    /// Get a field value by tag
67    pub fn get_field(&self, tag: u32) -> Option<&String> {
68        self.fields.get(&tag)
69    }
70
71    /// Set a field value
72    pub fn set_field(&mut self, tag: u32, value: String) {
73        self.fields.insert(tag, value);
74    }
75
76    /// Get message type
77    pub fn msg_type(&self) -> Option<MsgType> {
78        self.get_field(35)
79            .and_then(|s| MsgType::from_str(s))
80    }
81
82    /// Get sender company ID
83    pub fn sender_comp_id(&self) -> Option<&String> {
84        self.get_field(49)
85    }
86
87    /// Get target company ID
88    pub fn target_comp_id(&self) -> Option<&String> {
89        self.get_field(56)
90    }
91
92    /// Get message sequence number
93    pub fn msg_seq_num(&self) -> Option<u32> {
94        self.get_field(34)
95            .and_then(|s| s.parse().ok())
96    }
97
98    /// Convert to FIX string format
99    pub fn to_string(&self) -> String {
100        let mut message = String::new();
101        
102        // Standard FIX message order: BeginString, BodyLength, MsgType, then other fields
103        let ordered_tags = [8, 9, 35]; // BeginString, BodyLength, MsgType
104        
105        // Add ordered fields first
106        for &tag in &ordered_tags {
107            if let Some(value) = self.fields.get(&tag) {
108                message.push_str(&format!("{}={}\x01", tag, value));
109            }
110        }
111        
112        // Add remaining fields (except checksum which goes last)
113        for (&tag, value) in &self.fields {
114            if !ordered_tags.contains(&tag) && tag != 10 { // Skip checksum
115                message.push_str(&format!("{}={}\x01", tag, value));
116            }
117        }
118        
119        // Calculate and add checksum
120        let checksum = self.calculate_checksum(&message);
121        message.push_str(&format!("10={:03}\x01", checksum));
122        
123        message
124    }
125
126    /// Calculate FIX checksum
127    fn calculate_checksum(&self, message: &str) -> u8 {
128        let sum: u32 = message.bytes().map(|b| b as u32).sum();
129        (sum % 256) as u8
130    }
131
132    /// Validate message integrity
133    pub fn validate(&self) -> Result<()> {
134        // Check required fields
135        if !self.fields.contains_key(&8) {
136            return Err(DeribitFixError::MessageParsing("Missing BeginString".to_string()));
137        }
138        
139        if !self.fields.contains_key(&35) {
140            return Err(DeribitFixError::MessageParsing("Missing MsgType".to_string()));
141        }
142        
143        if !self.fields.contains_key(&49) {
144            return Err(DeribitFixError::MessageParsing("Missing SenderCompID".to_string()));
145        }
146        
147        if !self.fields.contains_key(&56) {
148            return Err(DeribitFixError::MessageParsing("Missing TargetCompID".to_string()));
149        }
150        
151        if !self.fields.contains_key(&34) {
152            return Err(DeribitFixError::MessageParsing("Missing MsgSeqNum".to_string()));
153        }
154
155        Ok(())
156    }
157}
158
159impl Default for FixMessage {
160    fn default() -> Self {
161        Self::new()
162    }
163}
164
165impl std::fmt::Display for FixMessage {
166    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167        write!(f, "{}", self.to_string())
168    }
169}
170
171/// Builder for constructing FIX messages
172pub struct MessageBuilder {
173    message: FixMessage,
174}
175
176impl MessageBuilder {
177    /// Create a new message builder
178    pub fn new() -> Self {
179        let mut message = FixMessage::new();
180        
181        // Set standard fields
182        message.set_field(8, "FIX.4.4".to_string()); // BeginString
183        
184        Self { message }
185    }
186
187    /// Set message type
188    pub fn msg_type(mut self, msg_type: MsgType) -> Self {
189        self.message.set_field(35, msg_type.as_str().to_string());
190        self
191    }
192
193    /// Set sender company ID
194    pub fn sender_comp_id(mut self, sender_comp_id: String) -> Self {
195        self.message.set_field(49, sender_comp_id);
196        self
197    }
198
199    /// Set target company ID
200    pub fn target_comp_id(mut self, target_comp_id: String) -> Self {
201        self.message.set_field(56, target_comp_id);
202        self
203    }
204
205    /// Set message sequence number
206    pub fn msg_seq_num(mut self, seq_num: u32) -> Self {
207        self.message.set_field(34, seq_num.to_string());
208        self
209    }
210
211    /// Set sending time
212    pub fn sending_time(mut self, time: DateTime<Utc>) -> Self {
213        let time_str = time.format("%Y%m%d-%H:%M:%S%.3f").to_string();
214        self.message.set_field(52, time_str);
215        self
216    }
217
218    /// Add a custom field
219    pub fn field(mut self, tag: u32, value: String) -> Self {
220        self.message.set_field(tag, value);
221        self
222    }
223
224    /// Build the message
225    pub fn build(mut self) -> Result<FixMessage> {
226        // Calculate body length (all fields except BeginString, BodyLength, and Checksum)
227        let temp_message = self.message.to_string();
228        let body_start = temp_message.find("35=").unwrap_or(0);
229        let body_end = temp_message.rfind("10=").unwrap_or(temp_message.len());
230        let body_length = body_end - body_start;
231        
232        self.message.set_field(9, body_length.to_string());
233        
234        // Validate the message
235        self.message.validate()?;
236        
237        Ok(self.message)
238    }
239}
240
241impl Default for MessageBuilder {
242    fn default() -> Self {
243        Self::new()
244    }
245}