1pub use crate::compat::{
5 ASSET_CSTR_LEN_V3 as ASSET_CSTR_LEN, SYMBOL_CSTR_LEN_V3 as SYMBOL_CSTR_LEN,
6 UNDEF_STAT_QUANTITY_V3 as UNDEF_STAT_QUANTITY,
7};
8pub use crate::record::{
9 Bbo1MMsg, Bbo1SMsg, BboMsg, Cbbo1MMsg, Cbbo1SMsg, CbboMsg, Cmbp1Msg, ErrorMsg, ImbalanceMsg,
10 InstrumentDefMsg, MboMsg, Mbp10Msg, Mbp1Msg, OhlcvMsg, StatMsg, StatusMsg, SymbolMappingMsg,
11 SystemMsg, TbboMsg, TcbboMsg, TradeMsg, WithTsOut,
12};
13
14use crate::compat::{InstrumentDefRec, StatRec};
15
16mod methods;
17
18pub const DBN_VERSION: u8 = 3;
20
21impl InstrumentDefRec for InstrumentDefMsg {
22 fn raw_symbol(&self) -> crate::Result<&str> {
23 Self::raw_symbol(self)
24 }
25
26 fn asset(&self) -> crate::Result<&str> {
27 Self::asset(self)
28 }
29
30 fn security_type(&self) -> crate::Result<&str> {
31 Self::security_type(self)
32 }
33
34 fn security_update_action(&self) -> crate::Result<crate::SecurityUpdateAction> {
35 Self::security_update_action(self)
36 }
37
38 fn channel_id(&self) -> u16 {
39 self.channel_id
40 }
41}
42
43impl StatRec for StatMsg {
44 const UNDEF_STAT_QUANTITY: i64 = UNDEF_STAT_QUANTITY;
45
46 fn stat_type(&self) -> crate::Result<crate::StatType> {
47 Self::stat_type(self)
48 }
49
50 fn ts_recv(&self) -> Option<time::OffsetDateTime> {
51 Self::ts_recv(self)
52 }
53
54 fn ts_ref(&self) -> Option<time::OffsetDateTime> {
55 Self::ts_ref(self)
56 }
57
58 fn update_action(&self) -> crate::Result<crate::StatUpdateAction> {
59 Self::update_action(self)
60 }
61
62 fn price(&self) -> i64 {
63 self.price
64 }
65
66 fn quantity(&self) -> i64 {
67 self.quantity
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use std::mem;
74
75 use rstest::*;
76 use type_layout::{Field, TypeLayout};
77
78 use super::*;
79
80 #[cfg(feature = "python")]
81 #[test]
82 fn test_consistent_field_order_and_leg_fields_last() {
83 use std::ops::Not;
84
85 use crate::{python::PyFieldDesc, v2};
86
87 let v3_fields = InstrumentDefMsg::ordered_fields("");
88 let mut v2_fields = v2::InstrumentDefMsg::ordered_fields("")
89 .into_iter()
90 .filter(|f| {
91 matches!(
92 f.as_str(),
93 "trading_reference_date"
94 | "trading_reference_price"
95 | "settl_price_type"
96 | "md_security_trading_status"
97 )
98 .not()
99 });
100 let mut has_reached_leg_fields = false;
101 for (i, field) in v3_fields.into_iter().enumerate() {
102 if has_reached_leg_fields {
103 assert!(field.starts_with("leg_"), "{i}");
104 } else if field.starts_with("leg_") {
105 has_reached_leg_fields = true;
106 assert!(v2_fields.next().is_none(), "{i}");
107 } else {
108 assert_eq!(field, v2_fields.next().unwrap(), "{i}");
109 }
110 }
111 }
112
113 #[rstest]
114 #[case::definition(InstrumentDefMsg::default(), 520)]
115 #[case::definition(StatMsg::default(), 80)]
116 fn test_sizes<R: Sized>(#[case] _rec: R, #[case] exp: usize) {
117 assert_eq!(mem::size_of::<R>(), exp);
118 assert!(mem::size_of::<R>() <= crate::MAX_RECORD_LEN);
119 }
120
121 #[rstest]
122 #[case::definition(InstrumentDefMsg::default())]
123 fn test_alignment_and_no_padding<R: TypeLayout>(#[case] _rec: R) {
124 let layout = R::type_layout();
125 assert_eq!(layout.alignment, 8, "Unexpected alignment: {layout}");
126 for field in layout.fields.iter() {
127 assert!(
128 matches!(field, Field::Field { .. }),
129 "Detected padding: {layout}"
130 );
131 }
132 }
133}