1use crate::types::Presence;
7
8#[derive(Debug, Clone)]
10pub struct MessageDef {
11 pub name: String,
13 pub id: u16,
15 pub block_length: u16,
17 pub semantic_type: Option<String>,
19 pub description: Option<String>,
21 pub since_version: Option<u16>,
23 pub deprecated: Option<u16>,
25 pub fields: Vec<FieldDef>,
27 pub groups: Vec<GroupDef>,
29 pub data_fields: Vec<DataFieldDef>,
31}
32
33impl MessageDef {
34 #[must_use]
36 pub fn new(name: String, id: u16, block_length: u16) -> Self {
37 Self {
38 name,
39 id,
40 block_length,
41 semantic_type: None,
42 description: None,
43 since_version: None,
44 deprecated: None,
45 fields: Vec::new(),
46 groups: Vec::new(),
47 data_fields: Vec::new(),
48 }
49 }
50
51 pub fn add_field(&mut self, field: FieldDef) {
53 self.fields.push(field);
54 }
55
56 pub fn add_group(&mut self, group: GroupDef) {
58 self.groups.push(group);
59 }
60
61 pub fn add_data_field(&mut self, data_field: DataFieldDef) {
63 self.data_fields.push(data_field);
64 }
65
66 #[must_use]
68 pub fn has_groups(&self) -> bool {
69 !self.groups.is_empty()
70 }
71
72 #[must_use]
74 pub fn has_var_data(&self) -> bool {
75 !self.data_fields.is_empty()
76 }
77
78 #[must_use]
80 pub fn min_encoded_length(&self) -> usize {
81 8 + self.block_length as usize }
83}
84
85#[derive(Debug, Clone)]
87pub struct FieldDef {
88 pub name: String,
90 pub id: u16,
92 pub type_name: String,
94 pub offset: usize,
96 pub presence: Presence,
98 pub semantic_type: Option<String>,
100 pub description: Option<String>,
102 pub since_version: Option<u16>,
104 pub deprecated: Option<u16>,
106 pub value_ref: Option<String>,
108 pub encoded_length: usize,
110}
111
112impl FieldDef {
113 #[must_use]
115 pub fn new(name: String, id: u16, type_name: String, offset: usize) -> Self {
116 Self {
117 name,
118 id,
119 type_name,
120 offset,
121 presence: Presence::Required,
122 semantic_type: None,
123 description: None,
124 since_version: None,
125 deprecated: None,
126 value_ref: None,
127 encoded_length: 0,
128 }
129 }
130
131 #[must_use]
133 pub fn is_optional(&self) -> bool {
134 self.presence == Presence::Optional
135 }
136
137 #[must_use]
139 pub fn is_constant(&self) -> bool {
140 self.presence == Presence::Constant
141 }
142
143 #[must_use]
145 pub fn end_offset(&self) -> usize {
146 self.offset + self.encoded_length
147 }
148}
149
150#[derive(Debug, Clone)]
152pub struct GroupDef {
153 pub name: String,
155 pub id: u16,
157 pub block_length: u16,
159 pub dimension_type: String,
161 pub description: Option<String>,
163 pub since_version: Option<u16>,
165 pub deprecated: Option<u16>,
167 pub fields: Vec<FieldDef>,
169 pub nested_groups: Vec<GroupDef>,
171 pub data_fields: Vec<DataFieldDef>,
173}
174
175impl GroupDef {
176 #[must_use]
178 pub fn new(name: String, id: u16, block_length: u16) -> Self {
179 Self {
180 name,
181 id,
182 block_length,
183 dimension_type: "groupSizeEncoding".to_string(),
184 description: None,
185 since_version: None,
186 deprecated: None,
187 fields: Vec::new(),
188 nested_groups: Vec::new(),
189 data_fields: Vec::new(),
190 }
191 }
192
193 pub fn add_field(&mut self, field: FieldDef) {
195 self.fields.push(field);
196 }
197
198 pub fn add_nested_group(&mut self, group: GroupDef) {
200 self.nested_groups.push(group);
201 }
202
203 pub fn add_data_field(&mut self, data_field: DataFieldDef) {
205 self.data_fields.push(data_field);
206 }
207
208 #[must_use]
210 pub fn has_nested_groups(&self) -> bool {
211 !self.nested_groups.is_empty()
212 }
213
214 #[must_use]
216 pub fn has_var_data(&self) -> bool {
217 !self.data_fields.is_empty()
218 }
219
220 #[must_use]
222 pub const fn header_size(&self) -> usize {
223 4 }
225}
226
227#[derive(Debug, Clone)]
229pub struct DataFieldDef {
230 pub name: String,
232 pub id: u16,
234 pub type_name: String,
236 pub description: Option<String>,
238 pub since_version: Option<u16>,
240 pub deprecated: Option<u16>,
242}
243
244impl DataFieldDef {
245 #[must_use]
247 pub fn new(name: String, id: u16, type_name: String) -> Self {
248 Self {
249 name,
250 id,
251 type_name,
252 description: None,
253 since_version: None,
254 deprecated: None,
255 }
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn test_message_def_creation() {
265 let mut msg = MessageDef::new("NewOrderSingle".to_string(), 1, 56);
266 msg.add_field(FieldDef::new(
267 "clOrdId".to_string(),
268 11,
269 "ClOrdId".to_string(),
270 0,
271 ));
272 msg.add_field(FieldDef::new(
273 "symbol".to_string(),
274 55,
275 "Symbol".to_string(),
276 20,
277 ));
278
279 assert_eq!(msg.name, "NewOrderSingle");
280 assert_eq!(msg.id, 1);
281 assert_eq!(msg.block_length, 56);
282 assert_eq!(msg.fields.len(), 2);
283 assert_eq!(msg.min_encoded_length(), 64); }
285
286 #[test]
287 fn test_field_def() {
288 let mut field = FieldDef::new("price".to_string(), 44, "decimal".to_string(), 30);
289 field.encoded_length = 9;
290 field.presence = Presence::Optional;
291
292 assert!(field.is_optional());
293 assert!(!field.is_constant());
294 assert_eq!(field.end_offset(), 39);
295 }
296
297 #[test]
298 fn test_group_def() {
299 let mut group = GroupDef::new("mdEntries".to_string(), 268, 31);
300 group.add_field(FieldDef::new(
301 "securityId".to_string(),
302 48,
303 "uint64".to_string(),
304 0,
305 ));
306 group.add_field(FieldDef::new(
307 "rptSeq".to_string(),
308 83,
309 "uint32".to_string(),
310 8,
311 ));
312
313 assert_eq!(group.name, "mdEntries");
314 assert_eq!(group.fields.len(), 2);
315 assert_eq!(group.header_size(), 4);
316 assert!(!group.has_nested_groups());
317 assert!(!group.has_var_data());
318 }
319
320 #[test]
321 fn test_data_field_def() {
322 let data = DataFieldDef::new("rawData".to_string(), 96, "varDataEncoding".to_string());
323 assert_eq!(data.name, "rawData");
324 assert_eq!(data.type_name, "varDataEncoding");
325 }
326
327 #[test]
328 fn test_message_with_groups_and_data() {
329 let mut msg = MessageDef::new("MarketDataRefresh".to_string(), 3, 16);
330 msg.add_group(GroupDef::new("mdEntries".to_string(), 268, 31));
331 msg.add_data_field(DataFieldDef::new(
332 "rawData".to_string(),
333 96,
334 "varDataEncoding".to_string(),
335 ));
336
337 assert!(msg.has_groups());
338 assert!(msg.has_var_data());
339 }
340}