use crate::error::{DeribitFixError, Result};
use crate::model::message::FixMessage;
use crate::model::types::MsgType;
use chrono::{DateTime, Utc};
pub struct MessageBuilder {
message: FixMessage,
}
impl MessageBuilder {
pub fn new() -> Self {
let mut message = FixMessage::new();
message.set_field(8, "FIX.4.4".to_string());
Self { message }
}
pub fn msg_type(mut self, msg_type: MsgType) -> Self {
self.message.set_field(35, msg_type.as_str().to_string());
self
}
pub fn sender_comp_id(mut self, sender_comp_id: String) -> Self {
self.message.set_field(49, sender_comp_id);
self
}
pub fn target_comp_id(mut self, target_comp_id: String) -> Self {
self.message.set_field(56, target_comp_id);
self
}
pub fn msg_seq_num(mut self, seq_num: u32) -> Self {
self.message.set_field(34, seq_num.to_string());
self
}
pub fn sending_time(mut self, time: DateTime<Utc>) -> Self {
let time_str = time.format("%Y%m%d-%H:%M:%S%.3f").to_string();
self.message.set_field(52, time_str);
self
}
pub fn field(mut self, tag: u32, value: String) -> Self {
self.message.set_field(tag, value);
self
}
pub fn build(mut self) -> Result<FixMessage> {
if !self.message.has_field(8) {
return Err(DeribitFixError::MessageConstruction(
"BeginString (8) is required".to_string(),
));
}
if !self.message.has_field(35) {
return Err(DeribitFixError::MessageConstruction(
"MsgType (35) is required".to_string(),
));
}
if !self.message.has_field(49) {
return Err(DeribitFixError::MessageConstruction(
"SenderCompID (49) is required".to_string(),
));
}
if !self.message.has_field(56) {
return Err(DeribitFixError::MessageConstruction(
"TargetCompID (56) is required".to_string(),
));
}
if !self.message.has_field(34) {
return Err(DeribitFixError::MessageConstruction(
"MsgSeqNum (34) is required".to_string(),
));
}
if !self.message.has_field(52) {
let now = Utc::now();
let time_str = now.format("%Y%m%d-%H:%M:%S%.3f").to_string();
self.message.set_field(52, time_str);
}
let body_length = self.calculate_body_length();
self.message.set_field(9, body_length.to_string());
let checksum = self.message.calculate_checksum();
self.message.set_field(10, format!("{checksum:03}"));
let mut field_pairs: Vec<_> = self.message.fields.iter().collect();
field_pairs.sort_by_key(|(tag, _)| *tag);
let mut raw_parts = Vec::new();
let mut checksum_part = None;
if let Some((_, value)) = field_pairs.iter().find(|(tag, _)| *tag == 8) {
raw_parts.push(format!("8={value}"));
}
if let Some((_, value)) = field_pairs.iter().find(|(tag, _)| *tag == 9) {
raw_parts.push(format!("9={value}"));
}
for (tag, value) in field_pairs {
if *tag == 10 {
checksum_part = Some(format!("{tag}={value}"));
} else if *tag != 8 && *tag != 9 {
raw_parts.push(format!("{tag}={value}"));
}
}
if let Some(checksum) = checksum_part {
raw_parts.push(checksum);
}
self.message.raw_message = raw_parts.join("\x01") + "\x01";
Ok(self.message)
}
fn calculate_body_length(&self) -> usize {
let mut body_parts = Vec::new();
for (tag, value) in &self.message.fields {
if *tag != 8 && *tag != 9 && *tag != 10 {
body_parts.push(format!("{tag}={value}"));
}
}
let body_str = body_parts.join("\x01") + "\x01";
body_str.len()
}
}
impl Default for MessageBuilder {
fn default() -> Self {
Self::new()
}
}