Skip to main content

hotfix_message/
message.rs

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