hotfix_message/
message.rs

1use std::io::Write;
2
3use crate::FieldType;
4use crate::builder::SOH;
5use crate::encoder::Encode;
6use crate::error::EncodingResult;
7use crate::field_map::{Field, FieldMap};
8use crate::parts::{Body, Header, Part, RepeatingGroup, Trailer};
9use crate::{HardCodedFixFieldDefinition, fix44};
10use hotfix_dictionary::{FieldLocation, IsFieldDefinition};
11
12pub struct Message {
13    pub(crate) header: Header,
14    pub(crate) body: Body,
15    pub(crate) trailer: Trailer,
16}
17
18impl Message {
19    pub fn new(begin_string: &str, message_type: &str) -> Self {
20        let mut msg = Self {
21            header: Header::default(),
22            body: Body::default(),
23            trailer: Trailer::default(),
24        };
25        msg.set(fix44::BEGIN_STRING, begin_string);
26        msg.set(fix44::MSG_TYPE, message_type);
27
28        msg
29    }
30
31    pub(crate) fn with_header(header: Header) -> Self {
32        Self {
33            header,
34            body: Body::default(),
35            trailer: Trailer::default(),
36        }
37    }
38
39    pub fn encode(&mut self, config: &Config) -> EncodingResult<Vec<u8>> {
40        let mut buffer = Vec::new();
41
42        self.trailer.pop(fix44::CHECK_SUM);
43        let body_length = self.header.calculate_length()
44            + self.body.calculate_length()
45            + self.trailer.calculate_length();
46        self.set(fix44::BODY_LENGTH, format!("{body_length}").as_str());
47        let check_sum_start = buffer.len();
48
49        let starting_fields = vec![
50            fix44::BEGIN_STRING.tag(),
51            fix44::BODY_LENGTH.tag(),
52            fix44::MSG_TYPE.tag(),
53        ];
54        self.header
55            .fields
56            .write(config, &mut buffer, &starting_fields)?;
57        self.body.fields.write(config, &mut buffer, &[])?;
58        self.trailer.fields.write(config, &mut buffer, &[])?;
59
60        let checksum = buffer.as_slice()[check_sum_start..]
61            .iter()
62            .fold(0u8, |acc, &x| acc.wrapping_add(x));
63        let checksum_value = format!("{checksum:03}");
64        self.set(fix44::CHECK_SUM, checksum_value.as_str());
65        buffer.write_all(b"10=")?;
66        buffer.write_all(checksum_value.as_bytes())?;
67        buffer.push(config.separator);
68
69        Ok(buffer)
70    }
71
72    pub fn header(&self) -> &Header {
73        &self.header
74    }
75
76    pub fn header_mut(&mut self) -> &mut Header {
77        &mut self.header
78    }
79
80    pub fn trailer(&self) -> &Trailer {
81        &self.trailer
82    }
83
84    pub fn get_group(
85        &self,
86        start_field: &HardCodedFixFieldDefinition,
87        index: usize,
88    ) -> Option<&RepeatingGroup> {
89        let tag = start_field.tag();
90        match start_field.location {
91            FieldLocation::Header => self.header.get_group(tag, index),
92            FieldLocation::Body => self.body.get_group(tag, index),
93            FieldLocation::Trailer => self.trailer.get_group(tag, index),
94        }
95    }
96}
97
98impl Part for Message {
99    fn get_field_map(&self) -> &FieldMap {
100        self.body.get_field_map()
101    }
102
103    fn get_field_map_mut(&mut self) -> &mut FieldMap {
104        self.body.get_field_map_mut()
105    }
106
107    fn set<'a, V>(&'a mut self, field_definition: &HardCodedFixFieldDefinition, value: V)
108    where
109        V: FieldType<'a>,
110    {
111        let field = Field::new(field_definition.tag(), value.to_bytes());
112
113        match field_definition.location {
114            FieldLocation::Header => self.header.store_field(field),
115            FieldLocation::Body => self.body.store_field(field),
116            FieldLocation::Trailer => self.trailer.store_field(field),
117        };
118    }
119}
120
121#[derive(Clone, Copy)]
122pub struct Config {
123    pub(crate) separator: u8,
124}
125
126impl Config {
127    pub const fn with_separator(separator: u8) -> Self {
128        Self { separator }
129    }
130}
131
132impl Default for Config {
133    fn default() -> Self {
134        Self { separator: SOH }
135    }
136}