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