hotfix_message/
message.rs

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