hotfix_message/
message.rs1use 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
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(BEGIN_STRING, begin_string);
27 msg.set(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 encode(&mut self, config: &Config) -> EncodingResult<Vec<u8>> {
41 let mut buffer = Vec::new();
42
43 self.trailer.pop(CHECK_SUM);
44 let body_length = self.header.calculate_length()
45 + self.body.calculate_length()
46 + self.trailer.calculate_length();
47 self.set(BODY_LENGTH, format!("{body_length}").as_str());
48 let check_sum_start = buffer.len();
49
50 let starting_fields = vec![BEGIN_STRING.tag(), BODY_LENGTH.tag(), MSG_TYPE.tag()];
51 self.header
52 .fields
53 .write(config, &mut buffer, &starting_fields)?;
54 self.body.fields.write(config, &mut buffer, &[])?;
55 self.trailer.fields.write(config, &mut buffer, &[])?;
56
57 let checksum = buffer.as_slice()[check_sum_start..]
58 .iter()
59 .fold(0u8, |acc, &x| acc.wrapping_add(x));
60 let checksum_value = format!("{checksum:03}");
61 self.set(CHECK_SUM, checksum_value.as_str());
62 buffer.write_all(b"10=")?;
63 buffer.write_all(checksum_value.as_bytes())?;
64 buffer.push(config.separator);
65
66 Ok(buffer)
67 }
68
69 pub fn header(&self) -> &Header {
70 &self.header
71 }
72
73 pub fn header_mut(&mut self) -> &mut Header {
74 &mut self.header
75 }
76
77 pub fn trailer(&self) -> &Trailer {
78 &self.trailer
79 }
80
81 pub fn get_group(
82 &self,
83 start_field: &HardCodedFixFieldDefinition,
84 index: usize,
85 ) -> Option<&RepeatingGroup> {
86 let tag = start_field.tag();
87 match start_field.location {
88 FieldLocation::Header => self.header.get_group(tag, index),
89 FieldLocation::Body => self.body.get_group(tag, index),
90 FieldLocation::Trailer => self.trailer.get_group(tag, index),
91 }
92 }
93}
94
95impl Part for Message {
96 fn get_field_map(&self) -> &FieldMap {
97 self.body.get_field_map()
98 }
99
100 fn get_field_map_mut(&mut self) -> &mut FieldMap {
101 self.body.get_field_map_mut()
102 }
103
104 fn set<'a, V>(&'a mut self, field_definition: &HardCodedFixFieldDefinition, value: V)
105 where
106 V: FieldType<'a>,
107 {
108 let field = Field::new(field_definition.tag(), value.to_bytes());
109
110 match field_definition.location {
111 FieldLocation::Header => self.header.store_field(field),
112 FieldLocation::Body => self.body.store_field(field),
113 FieldLocation::Trailer => self.trailer.store_field(field),
114 };
115 }
116}
117
118#[derive(Clone, Copy)]
119pub struct Config {
120 pub(crate) separator: u8,
121}
122
123impl Config {
124 pub const fn with_separator(separator: u8) -> Self {
125 Self { separator }
126 }
127}
128
129impl Default for Config {
130 fn default() -> Self {
131 Self { separator: SOH }
132 }
133}