pub mod field;
use bytes::Bytes;
use crate::{
decoder, encoder,
message::field::{
Field,
value::{begin_string::BeginString, msg_type::MsgType},
},
};
#[derive(Debug)]
pub struct Header {
#[allow(dead_code)]
pub(crate) begin_string: BeginString,
#[allow(dead_code)]
pub(crate) msg_type: MsgType,
pub(crate) fields: Vec<Field>,
}
#[derive(Default, Debug)]
pub struct Body {
pub(crate) fields: Vec<Field>,
}
#[derive(Debug)]
pub struct Message {
header: Header,
body: Body,
}
impl Message {
#[must_use]
pub fn builder(begin_string: BeginString, msg_type: MsgType) -> MessageBuilder<false> {
let header = Header {
begin_string,
msg_type,
fields: Vec::new(),
};
MessageBuilder {
inner: Message {
header,
body: Body::default(),
},
}
}
#[must_use]
pub fn encode(self) -> Bytes {
encoder::encode(&self.header, &self.body)
}
pub fn decode(input: impl AsRef<[u8]>) -> Result<Self, decoder::Error> {
decoder::decode(input)
}
}
pub struct MessageBuilder<const IS_INIT: bool> {
inner: Message,
}
impl<const IS_INIT: bool> MessageBuilder<IS_INIT> {
#[must_use]
pub fn with_header(mut self, field: Field) -> Self {
self.inner.header.fields.push(field);
self
}
#[must_use]
pub fn with_field(mut self, field: Field) -> MessageBuilder<true> {
self.inner.body.fields.push(field);
MessageBuilder { inner: self.inner }
}
}
impl MessageBuilder<true> {
#[must_use]
pub fn build(self) -> Message {
self.inner
}
}
#[cfg(test)]
mod test {
use crate::message::{
Message,
field::{
Field,
value::{begin_string::BeginString, msg_type::MsgType},
},
};
#[test]
fn basic_builder() {
let builder = Message::builder(BeginString::FIX44, MsgType::Logon);
assert_eq!(builder.inner.header.begin_string, BeginString::FIX44);
assert_eq!(builder.inner.header.msg_type, MsgType::Logon);
assert_eq!(builder.inner.body.fields.len(), 0);
}
#[test]
fn simple_message() {
let builder = Message::builder(BeginString::FIX44, MsgType::Logout);
let custom_header_field = Field::Custom {
tag: 22,
value: b"custom_header_field".to_vec(),
};
let custom_body_field1 = Field::Custom {
tag: 40000,
value: b"custom_body_field1".to_vec(),
};
let custom_body_field2 = Field::Custom {
tag: 50000,
value: b"custom_body_field2".to_vec(),
};
let msg = builder
.with_header(custom_header_field.clone())
.with_field(custom_body_field1.clone())
.with_field(custom_body_field2.clone())
.build();
assert_eq!(msg.header.begin_string, BeginString::FIX44);
assert_eq!(msg.header.msg_type, MsgType::Logout);
assert_eq!(msg.header.fields.clone().len(), 1);
assert_eq!(msg.header.fields[0], custom_header_field);
assert_eq!(msg.body.fields.len(), 2);
assert_eq!(msg.body.fields[0], custom_body_field1);
assert_eq!(msg.body.fields[1], custom_body_field2);
}
}