1mod builder;
4mod component;
5mod datatype;
6mod dictionary;
7mod field;
8mod layout;
9mod message_definition;
10mod quickfix;
11
12use component::{Component, ComponentData};
13use datatype::DatatypeData;
14pub use datatype::{Datatype, FixDatatype};
15pub use dictionary::Dictionary;
16pub use field::{Field, FieldEnum, FieldLocation, IsFieldDefinition};
17use field::{FieldData, FieldEnumData};
18use fnv::FnvHashMap;
19pub use layout::{LayoutItem, LayoutItemKind, display_layout_item};
20use layout::{LayoutItemData, LayoutItemKindData, LayoutItems};
21use std::sync::Arc;
22
23pub type Dictionaries = FnvHashMap<String, Arc<Dictionary>>;
25
26pub type TagU32 = std::num::NonZeroU32;
28
29#[cfg(test)]
30mod test {
31 use super::*;
32 use crate::layout::LayoutItemKind;
33 use std::collections::HashSet;
34
35 #[test]
36 fn fix44_quickfix_is_ok() {
37 let dict = Dictionary::fix44();
38 let msg_heartbeat = dict.message_by_name("Heartbeat").unwrap();
39 assert_eq!(msg_heartbeat.msg_type(), "0");
40 assert_eq!(msg_heartbeat.name(), "Heartbeat".to_string());
41 assert!(msg_heartbeat.layout().any(|c| {
42 if let LayoutItemKind::Field(f) = c.kind() {
43 f.name() == "TestReqID"
44 } else {
45 false
46 }
47 }));
48 }
49
50 #[test]
51 fn all_datatypes_are_used_at_least_once() {
52 for dict in Dictionary::common_dictionaries().iter() {
53 let datatypes_count = dict.datatypes().len();
54 let mut datatypes = HashSet::new();
55 for field in dict.fields() {
56 datatypes.insert(field.data_type().name().to_string());
57 }
58 assert_eq!(datatypes_count, datatypes.len());
59 }
60 }
61
62 #[test]
63 fn at_least_one_datatype() {
64 for dict in Dictionary::common_dictionaries().iter() {
65 assert!(!dict.datatypes().is_empty());
66 }
67 }
68
69 #[test]
70 fn std_header_and_trailer_always_present() {
71 for dict in Dictionary::common_dictionaries().iter() {
72 let std_header = dict.component_by_name("StandardHeader");
73 let std_trailer = dict.component_by_name("StandardTrailer");
74 assert!(std_header.is_some() && std_trailer.is_some());
75 }
76 }
77
78 #[test]
79 fn fix44_field_28_has_three_variants() {
80 let dict = Dictionary::fix44();
81 let field_28 = dict.field_by_tag(28).unwrap();
82 assert_eq!(field_28.name(), "IOITransType");
83 assert_eq!(field_28.enums().unwrap().count(), 3);
84 }
85
86 #[test]
87 fn fix44_field_36_has_no_variants() {
88 let dict = Dictionary::fix44();
89 let field_36 = dict.field_by_tag(36).unwrap();
90 assert_eq!(field_36.name(), "NewSeqNo");
91 assert!(field_36.enums().is_none());
92 }
93
94 #[test]
95 fn fix44_field_167_has_eucorp_variant() {
96 let dict = Dictionary::fix44();
97 let field_167 = dict.field_by_tag(167).unwrap();
98 assert_eq!(field_167.name(), "SecurityType");
99 assert!(field_167.enums().unwrap().any(|e| e.value() == "EUCORP"));
100 }
101
102 const INVALID_QUICKFIX_SPECS: &[&str] = &[
103 include_str!("test_data/quickfix_specs/empty_file.xml"),
104 include_str!("test_data/quickfix_specs/missing_components.xml"),
105 include_str!("test_data/quickfix_specs/missing_fields.xml"),
106 include_str!("test_data/quickfix_specs/missing_header.xml"),
107 include_str!("test_data/quickfix_specs/missing_messages.xml"),
108 include_str!("test_data/quickfix_specs/missing_trailer.xml"),
109 include_str!("test_data/quickfix_specs/root_has_no_type_attr.xml"),
110 include_str!("test_data/quickfix_specs/root_has_no_version_attrs.xml"),
111 include_str!("test_data/quickfix_specs/root_is_not_fix.xml"),
112 ];
113
114 #[test]
115 fn invalid_quickfix_specs() {
116 for spec in INVALID_QUICKFIX_SPECS.iter() {
117 let dict = Dictionary::from_quickfix_spec(spec);
118 assert!(dict.is_err(), "{}", spec);
119 }
120 }
121}