1#![feature(type_alias_impl_trait)]
2
3use std::{
4 collections::HashMap,
5 convert::{AsRef, TryFrom},
6 fmt,
7 ops::Deref,
8 str::FromStr,
9};
10
11use anyhow::{anyhow, bail, Context as ErrorContext, Result};
12use strum_macros::AsRefStr;
13use xmltree::{Element, XMLNode};
14
15type ElementIterator<'a> = impl Iterator<Item = &'a Element>;
16
17trait XmlHelper {
18 fn get_attribute(&self, attribute: &str) -> Result<&str>;
19 fn get_child_element(&self, child: &str) -> Result<&Element>;
20 fn get_child_elements(&self) -> ElementIterator;
21}
22
23impl XmlHelper for Element {
24 fn get_attribute(&self, attribute: &str) -> Result<&str> {
25 self.attributes
26 .get(attribute)
27 .map(String::as_ref)
28 .ok_or_else(|| anyhow!("no `{}` attribute in `{}` element", attribute, self.name))
29 }
30
31 fn get_child_element(&self, child: &str) -> Result<&Element> {
32 self.get_child(child)
33 .ok_or_else(|| anyhow!("no `{}` child in `{}` element", child, self.name))
34 }
35
36 fn get_child_elements(&self) -> ElementIterator {
37 self.children.iter().filter_map(XMLNode::as_element)
38 }
39}
40
41#[derive(Debug, PartialEq)]
42pub struct Version {
43 major: u32,
44 minor: u32,
45 service_pack: u32,
46}
47
48impl Version {
49 fn from_xml(element: &Element) -> Result<Version> {
50 Ok(Version {
51 major: element
52 .get_attribute("major")?
53 .parse()
54 .context("Failed to parse `major` number")?,
55 minor: element
56 .get_attribute("minor")?
57 .parse()
58 .context("Failed to parse `minor` number")?,
59 service_pack: element
60 .get_attribute("servicepack")?
61 .parse()
62 .context("Failed to parse `servicepack` number")?,
63 })
64 }
65
66 pub fn major(&self) -> u32 {
67 self.major
68 }
69
70 pub fn minor(&self) -> u32 {
71 self.minor
72 }
73
74 pub fn service_pack(&self) -> u32 {
75 self.service_pack
76 }
77}
78
79impl fmt::Display for Version {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 if self.service_pack == 0 {
82 write!(f, "{}.{}", self.major, self.minor)
83 } else {
84 write!(f, "{}.{} SP{}", self.major, self.minor, self.service_pack)
85 }
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq)]
90pub enum MemberKind {
91 Component,
92 Field,
93}
94
95#[derive(Debug, Clone, PartialEq)]
96pub struct Member {
97 name: String,
98 required: bool,
99 kind: MemberKind,
100}
101
102impl Member {
103 fn from_xml(element: &Element) -> Result<Member> {
104 let name = element.get_attribute("name")?;
105 if !name.is_ascii() {
106 bail!("Non ASCII characters in member name: {}", name);
107 }
108 let required = deserialize_yes_no(element.get_attribute("required")?)?;
109 let kind = match element.name.as_ref() {
110 "field" => MemberKind::Field,
111 "component" | "group" => MemberKind::Component,
112 name => bail!("Unexpected member kind `{}`", name),
113 };
114 Ok(Member {
115 name: name.into(),
116 required,
117 kind,
118 })
119 }
120
121 pub fn name(&self) -> &str {
122 &self.name
123 }
124
125 pub fn required(&self) -> bool {
126 self.required
127 }
128
129 pub fn kind(&self) -> MemberKind {
130 self.kind
131 }
132}
133
134#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
135pub enum BasicType {
136 Amt,
137 Boolean,
138 Char,
139 Country,
140 Currency,
141 Data,
142 Exchange,
143 Float,
144 Int,
145 Language,
146 Length,
147 LocalMktDate,
148 MonthYear,
149 MultipleCharValue,
150 MultipleStringValue,
151 NumInGroup,
152 Percentage,
153 Price,
154 PriceOffset,
155 Qty,
156 SeqNum,
157 String,
158 TzTimeOnly,
159 TzTimestamp,
160 UtcDateOnly,
161 UtcTimeOnly,
162 UtcTimestamp,
163 XmlData,
164}
165
166impl TryFrom<&str> for BasicType {
167 type Error = anyhow::Error;
168
169 fn try_from(input: &str) -> Result<Self, Self::Error> {
170 match input {
171 "AMT" => Ok(BasicType::Amt),
172 "BOOLEAN" => Ok(BasicType::Boolean),
173 "CHAR" => Ok(BasicType::Char),
174 "COUNTRY" => Ok(BasicType::Country),
175 "CURRENCY" => Ok(BasicType::Currency),
176 "DATA" => Ok(BasicType::Data),
177 "EXCHANGE" => Ok(BasicType::Exchange),
178 "FLOAT" => Ok(BasicType::Float),
179 "INT" | "LONG" => Ok(BasicType::Int),
180 "LANGUAGE" => Ok(BasicType::Language),
181 "LENGTH" => Ok(BasicType::Length),
182 "LOCALMKTDATE" => Ok(BasicType::LocalMktDate),
183 "MONTHYEAR" => Ok(BasicType::MonthYear),
184 "MULTIPLECHARVALUE" => Ok(BasicType::MultipleCharValue),
185 "MULTIPLESTRINGVALUE" => Ok(BasicType::MultipleStringValue),
186 "NUMINGROUP" => Ok(BasicType::NumInGroup),
187 "PERCENTAGE" => Ok(BasicType::Percentage),
188 "PRICE" => Ok(BasicType::Price),
189 "PRICEOFFSET" => Ok(BasicType::PriceOffset),
190 "QTY" => Ok(BasicType::Qty),
191 "SEQNUM" => Ok(BasicType::SeqNum),
192 "STRING" => Ok(BasicType::String),
193 "TZTIMEONLY" => Ok(BasicType::TzTimeOnly),
194 "TZTIMESTAMP" => Ok(BasicType::TzTimestamp),
195 "UTCDATEONLY" => Ok(BasicType::UtcDateOnly),
196 "UTCTIMEONLY" => Ok(BasicType::UtcTimeOnly),
197 "UTCTIMESTAMP" => Ok(BasicType::UtcTimestamp),
198 "XMLDATA" => Ok(BasicType::XmlData),
199 other => Err(anyhow!("Unexpected type `{}`", other)),
200 }
201 }
202}
203
204impl FromStr for BasicType {
205 type Err = anyhow::Error;
206
207 fn from_str(s: &str) -> Result<Self, Self::Err> {
208 TryFrom::try_from(s)
209 }
210}
211
212#[derive(Clone, Debug, PartialEq)]
213pub struct Value {
214 value: String,
215 description: String,
216}
217
218impl Value {
219 fn from_xml(element: &Element) -> Result<Value> {
220 if element.name != "value" {
221 bail!("Expected `value` node, found `{}`", element.name);
222 }
223
224 let value = element.get_attribute("enum")?;
225 if !value.is_ascii() {
226 bail!("Non ASCII characters in enum value: {}", value);
227 }
228
229 let description = element.get_attribute("description")?;
230 if !description.is_ascii() {
231 bail!("Non ASCII characters in enum description: {}", description);
232 }
233
234 Ok(Value {
235 value: value.into(),
236 description: description.into(),
237 })
238 }
239
240 pub fn value(&self) -> &str {
241 &self.value
242 }
243
244 pub fn description(&self) -> &str {
245 &self.description
246 }
247}
248
249#[derive(Clone, Debug, PartialEq)]
250pub struct Field {
251 name: String,
252 number: u16,
253 type_: BasicType,
254 values: Option<Vec<Value>>,
255}
256
257impl Field {
258 fn from_xml(element: &Element) -> Result<Field> {
259 let values = element
260 .get_child_elements()
261 .map(Value::from_xml)
262 .collect::<Result<Vec<_>, _>>()?;
263 let name = element.get_attribute("name")?;
264 if !name.is_ascii() {
265 bail!("Non ASCII characters in field name: {}", name);
266 }
267 Ok(Field {
268 name: name.into(),
269 number: element.get_attribute("number")?.parse()?,
270 type_: element.get_attribute("type")?.parse()?,
271 values: if values.is_empty() {
272 None
273 } else {
274 Some(values)
275 },
276 })
277 }
278
279 pub fn name(&self) -> &str {
280 &self.name
281 }
282
283 pub fn number(&self) -> u16 {
284 self.number
285 }
286
287 pub fn type_(&self) -> BasicType {
288 self.type_
289 }
290
291 pub fn values(&self) -> Option<&[Value]> {
292 self.values.as_deref()
293 }
294}
295
296fn deserialize_yes_no(input: &str) -> Result<bool> {
297 match input {
298 "Y" | "YES" | "y" | "yes" => Ok(true),
299 "N" | "NO" | "n" | "no" => Ok(false),
300 unexpected => Err(anyhow!(
301 "parse yes/no failed, unexpected value `{}`",
302 unexpected
303 )),
304 }
305}
306
307#[derive(Debug, Clone, PartialEq)]
308pub struct Component {
309 name: String,
310 number_of_elements: Option<Member>,
311 members: Vec<Member>,
312}
313
314impl Component {
315 fn from_xml(element: &Element) -> Result<Component> {
316 if element.name != "component" {
317 bail!("Expected `component` node, found `{}`", element.name);
318 }
319
320 let name = element.get_attribute("name")?.to_owned();
321 if !name.is_ascii() {
322 bail!("Non ASCII characters in component name: {}", name);
323 }
324
325 let mut iter = element.get_child_elements().peekable();
326
327 let number_of_elements = if let Some(child) = iter.peek() {
328 if child.name == "group" {
329 let member = Member::from_xml(child)?;
330 iter = child.get_child_elements().peekable();
331 Some(member)
332 } else {
333 None
334 }
335 } else {
336 bail!("Empty member list in `{}` component", name)
337 };
338
339 let members = iter.map(Member::from_xml).collect::<Result<Vec<_>, _>>()?;
340
341 Ok(Component {
342 name,
343 number_of_elements,
344 members,
345 })
346 }
347
348 fn from_header_or_trailer(element: &Element) -> Result<(Component, Vec<Component>)> {
351 let name = match element.name.as_str() {
352 "header" => "Header".to_owned(),
353 "trailer" => "Trailer".to_owned(),
354 unexpected => bail!("Expected `header/trailer` node, found `{}`", unexpected),
355 };
356
357 let mut groups = Vec::new();
358 let mut members = Vec::new();
359 for member_element in element.get_child_elements() {
360 if member_element.name == "group" {
361 let group_name = member_element.get_attribute("name")?;
362 let group_name = if group_name.starts_with("No") && group_name.ends_with('s') {
363 format!("{}Grp", &group_name[2..group_name.len() - 1])
364 } else {
365 bail!("Malformed group name `{}`", group_name);
366 };
367 let number_of_elements = Some(Member::from_xml(member_element)?);
368 let group_members = member_element
369 .get_child_elements()
370 .map(Member::from_xml)
371 .collect::<Result<Vec<_>, _>>()?;
372 groups.push(Component {
373 name: group_name.clone(),
374 number_of_elements,
375 members: group_members,
376 });
377 let mut member_element = member_element.clone();
378 if let Some(name) = member_element.attributes.get_mut("name") {
379 *name = group_name;
380 }
381 members.push(Member::from_xml(&member_element)?);
382 } else {
383 members.push(Member::from_xml(member_element)?);
384 }
385 }
386
387 Ok((
388 Component {
389 name,
390 number_of_elements: None,
391 members,
392 },
393 groups,
394 ))
395 }
396
397 pub fn name(&self) -> &str {
398 &self.name
399 }
400
401 pub fn number_of_elements(&self) -> Option<&Member> {
402 self.number_of_elements.as_ref()
403 }
404
405 pub fn members(&self) -> &[Member] {
406 &self.members
407 }
408}
409
410#[derive(Clone, Copy, Debug, PartialEq)]
411pub enum MsgCat {
412 Admin,
413 App,
414}
415
416#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
417enum MsgTypeBuf {
418 Short([u8; 1]),
419 Long([u8; 2]),
420}
421
422#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
423pub struct MsgType(MsgTypeBuf);
424
425impl Deref for MsgType {
426 type Target = [u8];
427
428 fn deref(&self) -> &Self::Target {
429 match self {
430 MsgType(MsgTypeBuf::Short(b)) => b,
431 MsgType(MsgTypeBuf::Long(b)) => b,
432 }
433 }
434}
435
436impl FromStr for MsgType {
437 type Err = anyhow::Error;
438
439 fn from_str(s: &str) -> Result<Self, Self::Err> {
440 if !s.is_ascii() {
441 bail!("Non ASCII characters in message type: {}", s);
442 }
443
444 match s.as_bytes() {
445 [] => Err(anyhow!("MsgType empty")),
446 [b0 @ b'0'..=b'9' | b0 @ b'A'..=b'Z' | b0 @ b'a'..=b'z'] => {
447 Ok(MsgType(MsgTypeBuf::Short([*b0])))
448 }
449 [b0 @ b'0'..=b'9' | b0 @ b'A'..=b'Z' | b0 @ b'a'..=b'z', b1 @ b'0'..=b'9' | b1 @ b'A'..=b'Z' | b1 @ b'a'..=b'z'] => {
450 Ok(MsgType(MsgTypeBuf::Long([*b0, *b1])))
451 }
452 [_] | [_, _] => Err(anyhow!("Incorrect MsgType value: {}", s)),
453 _ => Err(anyhow!("MsgType (`{}`) too long ({}`", s, s.len())),
454 }
455 }
456}
457
458impl FromStr for MsgCat {
459 type Err = anyhow::Error;
460
461 fn from_str(s: &str) -> Result<Self, Self::Err> {
462 match s {
463 "admin" => Ok(MsgCat::Admin),
464 "app" => Ok(MsgCat::App),
465 other => Err(anyhow!("Unknown message category `{}`", other)),
466 }
467 }
468}
469
470#[derive(Debug, PartialEq)]
471pub struct Message {
472 name: String,
473 msg_cat: MsgCat,
474 msg_type: MsgType,
475 members: Vec<Member>,
476}
477
478impl Message {
479 fn from_xml(element: &Element) -> Result<Message> {
480 if element.name != "message" {
481 bail!("Expected `message` node, found `{}`", element.name);
482 }
483
484 let name = element.get_attribute("name")?.to_owned();
485 if !name.is_ascii() {
486 bail!("Non ASCII characters in message name: {}", name);
487 }
488 let msg_cat = element.get_attribute("msgcat")?.parse()?;
489 let msg_type = element.get_attribute("msgtype")?.parse()?;
490
491 let members = element
492 .get_child_elements()
493 .map(Member::from_xml)
494 .collect::<Result<Vec<_>, _>>()?;
495
496 Ok(Message {
497 name,
498 msg_cat,
499 msg_type,
500 members,
501 })
502 }
503
504 pub fn name(&self) -> &str {
505 &self.name
506 }
507
508 pub fn msg_cat(&self) -> MsgCat {
509 self.msg_cat
510 }
511
512 pub fn msg_type(&self) -> MsgType {
513 self.msg_type
514 }
515
516 pub fn members(&self) -> &[Member] {
517 &self.members
518 }
519}
520
521#[derive(Debug, PartialEq)]
522pub struct Dictionary {
523 fix_version: Option<Version>,
524 fixt_version: Option<Version>,
525 header: Option<Component>,
526 trailer: Option<Component>,
527 messages: HashMap<MsgType, Message>,
528 flat_messages: HashMap<String, Message>,
529 components: Vec<Component>,
530 components_by_name: HashMap<String, Component>,
531 fields: HashMap<u16, Field>,
532 fields_by_name: HashMap<String, Field>,
533 reject_reason_overrides: HashMap<ParseRejectReason, String>,
534}
535
536impl Default for Dictionary {
537 fn default() -> Self {
538 Self::new(None)
539 }
540}
541
542impl Dictionary {
543 pub fn new(
544 optional_reject_reason_overrides: Option<HashMap<ParseRejectReason, String>>,
545 ) -> Dictionary {
546 Dictionary {
547 fixt_version: None,
548 fix_version: None,
549 header: None,
550 trailer: None,
551 messages: HashMap::new(),
552 flat_messages: HashMap::new(),
553 components: Vec::new(),
554 components_by_name: HashMap::new(),
555 fields: HashMap::new(),
556 fields_by_name: HashMap::new(),
557 reject_reason_overrides: optional_reject_reason_overrides.unwrap_or_default(),
558 }
559 }
560
561 pub fn process_legacy_fix_xml(&mut self, xml: &str) -> Result<()> {
562 let root = Element::parse(xml.as_bytes()).context("Failed to parse FIXT description")?;
563
564 let type_ = root.get_attribute("type")?;
565 if type_ != "FIX" {
566 bail!("Unexpected FIX XML description type `{}`", type_);
567 }
568
569 if self.fix_version.is_some() {
570 bail!("FIX XML already processed");
571 } else {
572 self.fix_version = Some(Version::from_xml(&root)?);
573 }
574
575 let (header, header_groups) =
576 Component::from_header_or_trailer(root.get_child_element("header")?)
577 .context("Failed to process FIX Header")?;
578 self.header = Some(header);
579 self.components.extend(header_groups);
580
581 let (trailer, trailer_groups) = Component::from_header_or_trailer(
582 root.get_child_element("trailer")
583 .context("Failed to process FIX trailer")?,
584 )?;
585 self.trailer = Some(trailer);
586 self.components.extend(trailer_groups);
587
588 self.process_common(&root)
589 }
590
591 pub fn process_fixt_xml(&mut self, xml: &str) -> Result<()> {
592 let root = Element::parse(xml.as_bytes()).context("Failed to parse FIXT description")?;
593
594 let type_ = root.get_attribute("type")?;
595 if type_ != "FIXT" {
596 bail!("Unexpected FIX XML description type `{}`", type_);
597 }
598
599 if self.fixt_version.is_some() {
600 bail!("FIXT XML already processed");
601 } else {
602 self.fixt_version = Some(Version::from_xml(&root)?);
603 }
604
605 let (header, header_groups) =
606 Component::from_header_or_trailer(root.get_child_element("header")?)
607 .context("Failed to process FIXT Header")?;
608 self.header = Some(header);
609 self.components.extend(header_groups);
610
611 let (trailer, trailer_groups) = Component::from_header_or_trailer(
612 root.get_child_element("trailer")
613 .context("Failed to process FIXT trailer")?,
614 )?;
615 self.trailer = Some(trailer);
616 self.components.extend(trailer_groups);
617
618 self.process_common(&root)
619 }
620
621 pub fn process_fix_xml(&mut self, xml: &str) -> Result<()> {
623 let root = Element::parse(xml.as_bytes()).context("Failed to parse FIX description")?;
624
625 let type_ = root.get_attribute("type")?;
626 if type_ != "FIX" {
627 bail!("Unexpected FIX XML description type `{}`", type_);
628 }
629
630 if self.fix_version.is_some() {
631 bail!("FIX XML already processed");
632 } else {
633 self.fix_version = Some(Version::from_xml(&root)?);
634 }
635
636 self.process_common(&root)
637 }
638
639 fn process_common(&mut self, root: &Element) -> Result<()> {
640 self.messages.extend(
641 root.get_child_element("messages")?
642 .get_child_elements()
643 .map(Message::from_xml)
644 .collect::<Result<Vec<_>, _>>()?
645 .into_iter()
646 .map(|m| (m.msg_type, m)),
647 );
648
649 self.components.extend(
650 root.get_child_element("components")?
651 .get_child_elements()
652 .map(Component::from_xml)
653 .collect::<Result<Vec<_>>>()?,
654 );
655 self.components_by_name.extend(
656 self.components
657 .iter()
658 .map(|c| (c.name().to_owned(), c.clone())),
659 );
660
661 self.fields.extend(
662 root.get_child_element("fields")?
663 .get_child_elements()
664 .map(Field::from_xml)
665 .collect::<Result<Vec<_>, _>>()?
666 .into_iter()
667 .map(|f| (f.number, f)),
668 );
669
670 let msg_type_field = self.fields.get_mut(&35).expect("MsgType field not defined");
672 msg_type_field.values = Some(
673 msg_type_field
674 .values
675 .take()
676 .expect("MsgType enum fields not defined")
677 .iter()
678 .filter(|v| {
679 self.messages
680 .contains_key(&MsgType::from_str(&v.value).expect("MsgType value error"))
681 })
682 .cloned()
683 .collect(),
684 );
685
686 self.fields_by_name.extend(
687 self.fields
688 .values()
689 .map(|f| (f.name().to_owned(), f.to_owned())),
690 );
691
692 Ok(())
693 }
694
695 pub fn fixt_version(&self) -> Option<&Version> {
696 self.fixt_version.as_ref()
697 }
698
699 pub fn fix_version(&self) -> Option<&Version> {
700 self.fix_version.as_ref()
701 }
702
703 pub fn header(&self) -> Result<&Component> {
704 self.header
705 .as_ref()
706 .ok_or_else(|| anyhow!("Missing header"))
707 }
708
709 pub fn trailer(&self) -> Result<&Component> {
710 self.trailer
711 .as_ref()
712 .ok_or_else(|| anyhow!("Missing trailer"))
713 }
714
715 pub fn components(&self) -> &[Component] {
716 &self.components
717 }
718
719 pub fn component(&self, name: &str) -> Option<&Component> {
720 self.components_by_name.get(name)
721 }
722
723 pub fn message(&self, name: &MsgType) -> Option<&Message> {
724 self.messages.get(name)
725 }
726
727 pub fn messages(&self) -> &HashMap<MsgType, Message> {
728 &self.messages
729 }
730
731 pub fn fields(&self) -> &HashMap<u16, Field> {
732 &self.fields
733 }
734
735 pub fn fields_by_name(&self) -> &HashMap<String, Field> {
736 &self.fields_by_name
737 }
738
739 pub fn reject_reason_overrides(&self) -> &HashMap<ParseRejectReason, String> {
740 &self.reject_reason_overrides
741 }
742}
743
744#[derive(Clone, Copy, Debug, Eq, PartialEq, strum_macros::EnumIter, AsRefStr, Hash)]
745pub enum ParseRejectReason {
746 ValueIsIncorrect,
747 TagSpecifiedWithoutAValue,
748 IncorrectDataFormatForValue,
749 TagAppearsMoreThanOnce,
750 TagSpecifiedOutOfRequiredOrder,
751 RequiredTagMissing,
752 IncorrectNumingroupCountForRepeatingGroup,
753 TagNotDefinedForThisMessageType,
754 UndefinedTag,
755 RepeatingGroupFieldsOutOfOrder,
756 InvalidTagNumber,
757 InvalidMsgtype,
758 SendingtimeAccuracyProblem,
759 CompidProblem,
760}
761
762#[cfg(test)]
763mod tests {
764
765 use std::str::FromStr;
766
767 use super::MsgType;
768
769 #[test]
770 fn parse_msg_type() {
771 assert!(MsgType::from_str("").is_err());
772 assert!(MsgType::from_str("\0").is_err());
773 assert!(MsgType::from_str("\0\0").is_err());
774 assert!(MsgType::from_str("\0\0\0").is_err());
775 assert!(MsgType::from_str("A").is_ok());
776 assert!(MsgType::from_str("AA").is_ok());
777 assert!(MsgType::from_str("AAA").is_err());
778 assert!(MsgType::from_str("\0A").is_err());
779 }
780}