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