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::Part;
56    use crate::field_map::Field;
57    use crate::field_types::{Date, Time, Timestamp};
58    use crate::fix44;
59    use crate::message::{Config, Message};
60    use crate::parts::RepeatingGroup;
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 dict = Dictionary::fix44();
82        let parsed_message = Message::from_bytes(&config, &dict, &raw_message)
83            .into_message()
84            .unwrap();
85
86        let symbol: &str = parsed_message.get(fix44::SYMBOL)?;
87        assert_eq!(symbol, "AAPL");
88
89        let qty: u32 = parsed_message.get(fix44::ORDER_QTY).unwrap();
90        assert_eq!(qty, 60);
91
92        let body_length: usize = parsed_message.header().get(fix44::BODY_LENGTH).unwrap();
93        assert_eq!(body_length, 129);
94
95        Ok(())
96    }
97
98    #[test]
99    fn encode_message_with_repeating_group() -> anyhow::Result<()> {
100        let sending_time = Timestamp::new(
101            Date::new(2023, 11, 7).unwrap(),
102            Time::from_hmsm(11, 0, 0, 0).unwrap(),
103        );
104        let mut msg = Message::new("FIX.4.4", "8");
105        msg.set(fix44::MSG_SEQ_NUM, 1);
106        msg.set(fix44::SENDER_COMP_ID, "BROKER_B");
107        msg.set(fix44::TARGET_COMP_ID, "CLIENT_A");
108        msg.set(fix44::SENDING_TIME, sending_time);
109        msg.set(fix44::CL_ORD_ID, "ORDER_0001");
110        msg.set(fix44::EXEC_ID, "Exec12345");
111        msg.set(fix44::ORD_STATUS, "0");
112        msg.set(fix44::SYMBOL, "AAPL");
113        msg.set(fix44::SIDE, fix44::Side::Buy);
114        msg.set(fix44::ORDER_QTY, 1000);
115        msg.set(fix44::LAST_QTY, 200);
116        msg.set(fix44::LAST_PX, 150.0);
117        msg.set(fix44::LEAVES_QTY, 800);
118        msg.set(fix44::CUM_QTY, 200);
119        msg.set(fix44::AVG_PX, 150.0);
120
121        msg.set(fix44::NO_PARTY_I_DS, 2);
122
123        let mut party_1 = RepeatingGroup::new(fix44::NO_PARTY_I_DS, fix44::PARTY_ID);
124        party_1.store_field(Field::new(fix44::PARTY_ID.tag(), b"PARTY_A".to_vec()));
125        party_1.store_field(Field::new(fix44::PARTY_ID_SOURCE.tag(), b"D".to_vec()));
126        party_1.store_field(Field::new(fix44::PARTY_ROLE.tag(), b"1".to_vec()));
127        party_1.store_field(Field::new(fix44::NO_PARTY_SUB_I_DS.tag(), b"2".to_vec()));
128
129        let mut subparty_1 = RepeatingGroup::new(fix44::NO_PARTY_SUB_I_DS, fix44::PARTY_SUB_ID);
130        subparty_1.store_field(Field::new(
131            fix44::PARTY_SUB_ID.tag(),
132            b"SUBPARTY_A_1".to_vec(),
133        ));
134        subparty_1.store_field(Field::new(fix44::PARTY_SUB_ID_TYPE.tag(), b"1".to_vec()));
135
136        let mut subparty_2 = RepeatingGroup::new(fix44::NO_PARTY_SUB_I_DS, fix44::PARTY_SUB_ID);
137        subparty_2.store_field(Field::new(
138            fix44::PARTY_SUB_ID.tag(),
139            b"SUBPARTY_A_2".to_vec(),
140        ));
141        subparty_2.store_field(Field::new(fix44::PARTY_SUB_ID_TYPE.tag(), b"2".to_vec()));
142
143        party_1.set_groups(vec![subparty_1, subparty_2]);
144
145        let mut party_2 = RepeatingGroup::new(fix44::NO_PARTY_I_DS, fix44::PARTY_ID);
146        party_2.store_field(Field::new(fix44::PARTY_ID.tag(), b"PARTY_B".to_vec()));
147        party_2.store_field(Field::new(fix44::PARTY_ID_SOURCE.tag(), b"D".to_vec()));
148        party_2.store_field(Field::new(fix44::PARTY_ROLE.tag(), b"2".to_vec()));
149
150        msg.body.set_groups(vec![party_1, party_2]);
151        let config = Config { separator: b'|' };
152        let raw_message = msg.encode(&config)?;
153
154        let dict = Dictionary::fix44();
155        let parsed_message = Message::from_bytes(&config, &dict, &raw_message)
156            .into_message()
157            .unwrap();
158
159        let party_a = parsed_message
160            .get_group(fix44::NO_PARTY_I_DS, 0)
161            .expect("group to be found");
162        let party_a_0 = party_a
163            .get_group(fix44::NO_PARTY_SUB_I_DS.tag(), 0)
164            .expect("group to be found");
165        let sub_id_0: &str = party_a_0.get(fix44::PARTY_SUB_ID)?;
166        assert_eq!(sub_id_0, "SUBPARTY_A_1");
167
168        let party_b = parsed_message
169            .get_group(fix44::NO_PARTY_I_DS, 1)
170            .expect("group to be found");
171        let party_b_id: &str = party_b.get(fix44::PARTY_ID)?;
172        assert_eq!(party_b_id, "PARTY_B");
173
174        let party_b_role: &str = party_b.get(fix44::PARTY_ROLE)?;
175        assert_eq!(party_b_role, "2");
176
177        let checksum: &str = parsed_message.trailer().get(fix44::CHECK_SUM)?;
178        assert_eq!(checksum, "036");
179
180        let qty: usize = parsed_message.header().get(fix44::BODY_LENGTH).unwrap();
181        assert_eq!(qty, 253);
182
183        Ok(())
184    }
185}