Skip to main content

hotfix_message/
encoder.rs

1use crate::error::EncodingResult;
2use crate::field_map::{Field, FieldMap};
3use crate::message::Config;
4use hotfix_dictionary::TagU32;
5use std::io::Write;
6
7pub trait Encode {
8    fn write(
9        &self,
10        config: &Config,
11        buffer: &mut Vec<u8>,
12        pre_fields: &[TagU32],
13    ) -> EncodingResult<()>;
14}
15
16impl Encode for FieldMap {
17    fn write(
18        &self,
19        config: &Config,
20        buffer: &mut Vec<u8>,
21        pre_fields: &[TagU32],
22    ) -> EncodingResult<()> {
23        let mut write_field = |field: &Field| {
24            let formatted_tag = format!("{}=", field.tag.get());
25            buffer.write_all(formatted_tag.as_bytes())?;
26            buffer.write_all(&field.data)?;
27            buffer.push(config.separator);
28
29            if let Some(groups) = self.groups.get(&field.tag) {
30                for group in groups {
31                    group.get_fields().write(config, buffer, &[])?;
32                }
33            }
34            Ok::<(), crate::error::EncodingError>(())
35        };
36
37        for pre_field_tag in pre_fields {
38            if let Some(field) = self.fields.get(pre_field_tag) {
39                write_field(field)?;
40            }
41        }
42
43        for (tag, field) in &self.fields {
44            if !pre_fields.contains(tag) {
45                write_field(field)?;
46            }
47        }
48
49        Ok(())
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use crate::field_map::Field;
56    use crate::field_types::{Date, Time, Timestamp};
57    use crate::fix44;
58    use crate::message::{Config, Message};
59    use crate::parts::RepeatingGroup;
60    use crate::{MessageBuilder, Part};
61    use hotfix_dictionary::{Dictionary, IsFieldDefinition};
62
63    #[test]
64    fn encode_simple_message() -> anyhow::Result<()> {
65        let mut msg = Message::new("FIX.4.4", "D");
66        msg.set(fix44::MSG_SEQ_NUM, 1);
67        msg.set(fix44::SENDER_COMP_ID, "CLIENT_A");
68        msg.set(fix44::TARGET_COMP_ID, "BROKER_B");
69        msg.set(fix44::SENDING_TIME, Timestamp::utc_now());
70        msg.set(fix44::CL_ORD_ID, "ORDER_0001");
71        msg.set(fix44::SYMBOL, "AAPL");
72        msg.set(fix44::SIDE, fix44::Side::Buy);
73        msg.set(fix44::TRANSACT_TIME, Timestamp::utc_now());
74        msg.set(fix44::ORD_TYPE, fix44::OrdType::Limit);
75        msg.set(fix44::PRICE, 150);
76        msg.set(fix44::ORDER_QTY, 60);
77
78        let config = Config { separator: b'|' };
79        let raw_message = msg.encode(&config)?;
80
81        let builder = MessageBuilder::new(Dictionary::fix44(), config)?;
82        let parsed_message = builder.build(&raw_message).into_message().unwrap();
83
84        let symbol: &str = parsed_message.get(fix44::SYMBOL)?;
85        assert_eq!(symbol, "AAPL");
86
87        let qty: u32 = parsed_message.get(fix44::ORDER_QTY).unwrap();
88        assert_eq!(qty, 60);
89
90        let body_length: usize = parsed_message.header().get(fix44::BODY_LENGTH).unwrap();
91        assert_eq!(body_length, 129);
92
93        Ok(())
94    }
95
96    #[test]
97    fn encode_message_with_repeating_group() -> anyhow::Result<()> {
98        let sending_time = Timestamp::new(
99            Date::new(2023, 11, 7).unwrap(),
100            Time::from_hmsm(11, 0, 0, 0).unwrap(),
101        );
102        let mut msg = Message::new("FIX.4.4", "8");
103        msg.set(fix44::MSG_SEQ_NUM, 1);
104        msg.set(fix44::SENDER_COMP_ID, "BROKER_B");
105        msg.set(fix44::TARGET_COMP_ID, "CLIENT_A");
106        msg.set(fix44::SENDING_TIME, sending_time);
107        msg.set(fix44::CL_ORD_ID, "ORDER_0001");
108        msg.set(fix44::EXEC_ID, "Exec12345");
109        msg.set(fix44::ORD_STATUS, "0");
110        msg.set(fix44::SYMBOL, "AAPL");
111        msg.set(fix44::SIDE, fix44::Side::Buy);
112        msg.set(fix44::ORDER_QTY, 1000);
113        msg.set(fix44::LAST_QTY, 200);
114        msg.set(fix44::LAST_PX, 150.0);
115        msg.set(fix44::LEAVES_QTY, 800);
116        msg.set(fix44::CUM_QTY, 200);
117        msg.set(fix44::AVG_PX, 150.0);
118
119        msg.set(fix44::NO_PARTY_I_DS, 2);
120
121        let mut party_1 = RepeatingGroup::new(fix44::NO_PARTY_I_DS, fix44::PARTY_ID);
122        party_1.store_field(Field::new(fix44::PARTY_ID.tag(), b"PARTY_A".to_vec()));
123        party_1.store_field(Field::new(fix44::PARTY_ID_SOURCE.tag(), b"D".to_vec()));
124        party_1.store_field(Field::new(fix44::PARTY_ROLE.tag(), b"1".to_vec()));
125        party_1.store_field(Field::new(fix44::NO_PARTY_SUB_I_DS.tag(), b"2".to_vec()));
126
127        let mut subparty_1 = RepeatingGroup::new(fix44::NO_PARTY_SUB_I_DS, fix44::PARTY_SUB_ID);
128        subparty_1.store_field(Field::new(
129            fix44::PARTY_SUB_ID.tag(),
130            b"SUBPARTY_A_1".to_vec(),
131        ));
132        subparty_1.store_field(Field::new(fix44::PARTY_SUB_ID_TYPE.tag(), b"1".to_vec()));
133
134        let mut subparty_2 = RepeatingGroup::new(fix44::NO_PARTY_SUB_I_DS, fix44::PARTY_SUB_ID);
135        subparty_2.store_field(Field::new(
136            fix44::PARTY_SUB_ID.tag(),
137            b"SUBPARTY_A_2".to_vec(),
138        ));
139        subparty_2.store_field(Field::new(fix44::PARTY_SUB_ID_TYPE.tag(), b"2".to_vec()));
140
141        party_1.set_groups(vec![subparty_1, subparty_2])?;
142
143        let mut party_2 = RepeatingGroup::new(fix44::NO_PARTY_I_DS, fix44::PARTY_ID);
144        party_2.store_field(Field::new(fix44::PARTY_ID.tag(), b"PARTY_B".to_vec()));
145        party_2.store_field(Field::new(fix44::PARTY_ID_SOURCE.tag(), b"D".to_vec()));
146        party_2.store_field(Field::new(fix44::PARTY_ROLE.tag(), b"2".to_vec()));
147
148        msg.body.set_groups(vec![party_1, party_2])?;
149        let config = Config { separator: b'|' };
150        let raw_message = msg.encode(&config)?;
151
152        let builder = MessageBuilder::new(Dictionary::fix44(), config)?;
153        let parsed_message = builder.build(&raw_message).into_message().unwrap();
154
155        let party_a = parsed_message
156            .get_group(fix44::NO_PARTY_I_DS, 0)
157            .expect("group to be found");
158        let party_a_0 = party_a
159            .get_group(fix44::NO_PARTY_SUB_I_DS.tag(), 0)
160            .expect("group to be found");
161        let sub_id_0: &str = party_a_0.get(fix44::PARTY_SUB_ID)?;
162        assert_eq!(sub_id_0, "SUBPARTY_A_1");
163
164        let party_b = parsed_message
165            .get_group(fix44::NO_PARTY_I_DS, 1)
166            .expect("group to be found");
167        let party_b_id: &str = party_b.get(fix44::PARTY_ID)?;
168        assert_eq!(party_b_id, "PARTY_B");
169
170        let party_b_role: &str = party_b.get(fix44::PARTY_ROLE)?;
171        assert_eq!(party_b_role, "2");
172
173        let checksum: &str = parsed_message.trailer().get(fix44::CHECK_SUM)?;
174        assert_eq!(checksum, "036");
175
176        let qty: usize = parsed_message.header().get(fix44::BODY_LENGTH).unwrap();
177        assert_eq!(qty, 253);
178
179        Ok(())
180    }
181}