use crate::{EncodeError, FieldValue, FixmlError, FixmlMessage, MessageType};
use quick_xml::{
events::{BytesEnd, BytesStart, Event},
Writer,
};
use std::io::Cursor;
#[derive(Debug, Default)]
pub struct EncodeConfig {
pub pretty_print: bool,
pub include_schema: bool,
}
#[derive(Debug)]
pub struct FixmlEncoder {
config: EncodeConfig,
}
impl FixmlEncoder {
pub fn new() -> Self {
Self {
config: EncodeConfig::default(),
}
}
pub fn encode(&self, message: &FixmlMessage) -> Result<String, FixmlError> {
message.validate()?;
let mut writer = Writer::new(Cursor::new(Vec::new()));
writer.write_event(Event::Decl(quick_xml::events::BytesDecl::new(
"1.0",
Some("UTF-8"),
None,
)))?;
let mut fixml_start = BytesStart::new("FIXML");
fixml_start.push_attribute(("xmlns", "http://www.fixprotocol.org/FIXML-4-4"));
writer.write_event(Event::Start(fixml_start))?;
let message_tag = self.get_message_tag(&message.message_type)?;
let mut msg_start = BytesStart::new(message_tag);
for (name, value) in &message.fields {
let (attr_name, value_str) = self.format_field_with_type_hint(name, value);
msg_start.push_attribute((attr_name.as_str(), value_str.as_str()));
}
writer.write_event(Event::Empty(msg_start))?;
writer.write_event(Event::End(BytesEnd::new("FIXML")))?;
let result = writer.into_inner().into_inner();
String::from_utf8(result).map_err(FixmlError::from)
}
fn format_field_with_type_hint(&self, name: &str, value: &FieldValue) -> (String, String) {
match value {
FieldValue::String(s) => (format!("{name}:String"), s.clone()),
FieldValue::Integer(i) => (format!("{name}:Integer"), i.to_string()),
FieldValue::Decimal(d) => (format!("{name}:Decimal"), d.to_string()),
FieldValue::Boolean(b) => (format!("{name}:Boolean"), b.to_string()),
}
}
fn get_message_tag(&self, msg_type: &MessageType) -> Result<&'static str, EncodeError> {
match msg_type {
MessageType::NewOrderSingle => Ok("Order"),
MessageType::ExecutionReport => Ok("ExecRpt"),
MessageType::MarketDataRequest => Ok("MktDataReq"),
MessageType::Heartbeat => Ok("Heartbeat"),
MessageType::Custom(_) => Err(EncodeError::UnsupportedMessageType {
message_type: "Custom".to_string(),
}),
}
}
}
impl Default for FixmlEncoder {
fn default() -> Self {
Self::new()
}
}