dbn/
record_enum.rs

1use crate::{
2    record::{CbboMsg, Cmbp1Msg},
3    BboMsg, Error, ErrorMsg, ImbalanceMsg, InstrumentDefMsg, MboMsg, Mbp10Msg, Mbp1Msg, OhlcvMsg,
4    RType, Record, RecordMut, RecordRef, StatMsg, StatusMsg, SymbolMappingMsg, SystemMsg, TradeMsg,
5};
6
7/// An owned DBN record type of flexible type. Unlike [`RecordRef`], this type allows
8/// `match`ing.
9///
10/// Note: this type does not support `ts_out`.
11#[derive(Debug, Clone)]
12pub enum RecordEnum {
13    /// An market-by-order message.
14    Mbo(MboMsg),
15    /// A trade message.
16    Trade(TradeMsg),
17    /// A market-by-price message with a book depth of 1.
18    Mbp1(Mbp1Msg),
19    /// A market-by-price message with a book depth of 10.
20    Mbp10(Mbp10Msg),
21    /// An open, high, low, close, and volume message.
22    Ohlcv(OhlcvMsg),
23    /// A trading status message.
24    Status(StatusMsg),
25    /// An instrument definition message.
26    InstrumentDef(InstrumentDefMsg),
27    /// An auction imbalance message.
28    Imbalance(ImbalanceMsg),
29    /// A publisher statistic message.
30    Stat(StatMsg),
31    /// An error message from the Databento Live Subscription Gateway (LSG).
32    Error(ErrorMsg),
33    /// A symbol mapping message.
34    SymbolMapping(SymbolMappingMsg),
35    /// A non-error message from the Databento Live Subscription Gateway (LSG).
36    System(SystemMsg),
37    /// A consolidated best bid and offer message.
38    Cmbp1(Cmbp1Msg),
39    /// A subsampled market-by-price message with a book depth of 1.
40    Bbo(BboMsg),
41    /// A subsampled and consolidated market-by-price message with a book depth of 1.
42    Cbbo(CbboMsg),
43}
44
45/// An immutable reference to a DBN record of flexible type. Unlike [`RecordRef`], this
46/// type allows `match`ing.
47///
48/// Note: this type does not support `ts_out`.
49#[derive(Debug, Copy, Clone)]
50pub enum RecordRefEnum<'a> {
51    /// A reference to a market-by-order message.
52    Mbo(&'a MboMsg),
53    /// A reference to a trade message.
54    Trade(&'a TradeMsg),
55    /// A reference to a market-by-price message with a book depth of 1.
56    Mbp1(&'a Mbp1Msg),
57    /// A reference to a market-by-price message with a book depth of 10.
58    Mbp10(&'a Mbp10Msg),
59    /// A reference to an open, high, low, close, and volume message.
60    Ohlcv(&'a OhlcvMsg),
61    /// A reference to a trading status message.
62    Status(&'a StatusMsg),
63    /// A reference to an instrument definition message.
64    InstrumentDef(&'a InstrumentDefMsg),
65    /// A reference to an auction imbalance message.
66    Imbalance(&'a ImbalanceMsg),
67    /// A reference to a publisher statistic message.
68    Stat(&'a StatMsg),
69    /// A reference to an error message from the Databento Live Subscription Gateway
70    /// (LSG).
71    Error(&'a ErrorMsg),
72    /// A reference to a symbol mapping message.
73    SymbolMapping(&'a SymbolMappingMsg),
74    /// A reference to a non-error message from the Databento Live Subscription Gateway
75    /// (LSG).
76    System(&'a SystemMsg),
77    /// A reference to a consolidated best bid and offer message.
78    Cmbp1(&'a Cmbp1Msg),
79    /// A subsampled market-by-price message with a book depth of 1.
80    Bbo(&'a BboMsg),
81    /// A subsampled and consolidated market-by-price message with a book depth of 1.
82    Cbbo(&'a CbboMsg),
83}
84
85impl<'a> From<&'a RecordEnum> for RecordRefEnum<'a> {
86    fn from(rec_enum: &'a RecordEnum) -> Self {
87        match rec_enum {
88            RecordEnum::Mbo(rec) => Self::Mbo(rec),
89            RecordEnum::Trade(rec) => Self::Trade(rec),
90            RecordEnum::Mbp1(rec) => Self::Mbp1(rec),
91            RecordEnum::Mbp10(rec) => Self::Mbp10(rec),
92            RecordEnum::Ohlcv(rec) => Self::Ohlcv(rec),
93            RecordEnum::Status(rec) => Self::Status(rec),
94            RecordEnum::InstrumentDef(rec) => Self::InstrumentDef(rec),
95            RecordEnum::Imbalance(rec) => Self::Imbalance(rec),
96            RecordEnum::Stat(rec) => Self::Stat(rec),
97            RecordEnum::Error(rec) => Self::Error(rec),
98            RecordEnum::SymbolMapping(rec) => Self::SymbolMapping(rec),
99            RecordEnum::System(rec) => Self::System(rec),
100            RecordEnum::Cmbp1(rec) => Self::Cmbp1(rec),
101            RecordEnum::Bbo(rec) => Self::Bbo(rec),
102            RecordEnum::Cbbo(rec) => Self::Cbbo(rec),
103        }
104    }
105}
106
107impl RecordRefEnum<'_> {
108    /// Converts the reference enum into an owned enum value.
109    pub fn to_owned(&self) -> RecordEnum {
110        #[allow(clippy::clone_on_copy)] // required for when trivial_copy feature is disabled
111        match self {
112            Self::Mbo(rec) => RecordEnum::from((*rec).clone()),
113            Self::Trade(rec) => RecordEnum::from((*rec).clone()),
114            Self::Mbp1(rec) => RecordEnum::from((*rec).clone()),
115            Self::Mbp10(rec) => RecordEnum::from((*rec).clone()),
116            Self::Ohlcv(rec) => RecordEnum::from((*rec).clone()),
117            Self::Status(rec) => RecordEnum::from((*rec).clone()),
118            Self::InstrumentDef(rec) => RecordEnum::from((*rec).clone()),
119            Self::Imbalance(rec) => RecordEnum::from((*rec).clone()),
120            Self::Stat(rec) => RecordEnum::from((*rec).clone()),
121            Self::Error(rec) => RecordEnum::from((*rec).clone()),
122            Self::SymbolMapping(rec) => RecordEnum::from((*rec).clone()),
123            Self::System(rec) => RecordEnum::from((*rec).clone()),
124            Self::Cmbp1(rec) => RecordEnum::from((*rec).clone()),
125            Self::Bbo(rec) => RecordEnum::Bbo((*rec).clone()),
126            Self::Cbbo(rec) => RecordEnum::Cbbo((*rec).clone()),
127        }
128    }
129}
130
131impl<'a> TryFrom<RecordRef<'a>> for RecordRefEnum<'a> {
132    type Error = Error;
133
134    fn try_from(rec_ref: RecordRef<'a>) -> Result<Self, Error> {
135        Ok(unsafe {
136            #[allow(deprecated)]
137            match rec_ref.rtype()? {
138                RType::Mbo => RecordRefEnum::Mbo(rec_ref.get_unchecked()),
139                RType::Mbp0 => RecordRefEnum::Trade(rec_ref.get_unchecked()),
140                RType::Mbp1 => RecordRefEnum::Mbp1(rec_ref.get_unchecked()),
141                RType::Bbo1S | RType::Bbo1M => RecordRefEnum::Bbo(rec_ref.get_unchecked()),
142                RType::Mbp10 => RecordRefEnum::Mbp10(rec_ref.get_unchecked()),
143                RType::OhlcvDeprecated
144                | RType::Ohlcv1S
145                | RType::Ohlcv1M
146                | RType::Ohlcv1H
147                | RType::Ohlcv1D
148                | RType::OhlcvEod => RecordRefEnum::Ohlcv(rec_ref.get_unchecked()),
149                RType::Status => RecordRefEnum::Status(rec_ref.get_unchecked()),
150                RType::InstrumentDef => {
151                    // can't convert V1 structs here because an immutable reference
152                    if rec_ref.record_size() < std::mem::size_of::<InstrumentDefMsg>() {
153                        return Err(Error::conversion::<Self>(
154                            "earlier version of InstrumentDefMsg (must be current version)",
155                        ));
156                    }
157                    RecordRefEnum::InstrumentDef(rec_ref.get_unchecked())
158                }
159                RType::Imbalance => RecordRefEnum::Imbalance(rec_ref.get_unchecked()),
160                RType::Statistics => {
161                    if rec_ref.record_size() < std::mem::size_of::<StatMsg>() {
162                        return Err(Error::conversion::<Self>(
163                            "earlier version of StatMsg (must be current version)",
164                        ));
165                    }
166                    RecordRefEnum::Stat(rec_ref.get_unchecked())
167                }
168                RType::Error => {
169                    // can't convert V1 structs here because an immutable reference
170                    if rec_ref.record_size() < std::mem::size_of::<ErrorMsg>() {
171                        return Err(Error::conversion::<Self>(
172                            "earlier version of ErrorMsg (must be current version)",
173                        ));
174                    }
175                    RecordRefEnum::Error(rec_ref.get_unchecked())
176                }
177                RType::SymbolMapping => {
178                    // can't convert V1 structs here because an immutable reference
179                    if rec_ref.record_size() < std::mem::size_of::<SymbolMappingMsg>() {
180                        return Err(Error::conversion::<Self>(
181                            "earlier version of SymbolMappingMsg (must be current version)",
182                        ));
183                    }
184                    RecordRefEnum::SymbolMapping(rec_ref.get_unchecked())
185                }
186                RType::System => {
187                    // can't convert V1 structs here because an immutable reference
188                    if rec_ref.record_size() < std::mem::size_of::<SystemMsg>() {
189                        return Err(Error::conversion::<Self>(
190                            "earlier version of SystemMsg (must be current version)",
191                        ));
192                    }
193                    RecordRefEnum::System(rec_ref.get_unchecked())
194                }
195                RType::Cmbp1 | RType::Tcbbo => RecordRefEnum::Cmbp1(rec_ref.get_unchecked()),
196                RType::Cbbo1S | RType::Cbbo1M => RecordRefEnum::Cbbo(rec_ref.get_unchecked()),
197            }
198        })
199    }
200}
201
202impl From<MboMsg> for RecordEnum {
203    fn from(rec: MboMsg) -> Self {
204        Self::Mbo(rec)
205    }
206}
207impl<'a> From<&'a MboMsg> for RecordRefEnum<'a> {
208    fn from(rec: &'a MboMsg) -> Self {
209        Self::Mbo(rec)
210    }
211}
212impl From<TradeMsg> for RecordEnum {
213    fn from(rec: TradeMsg) -> Self {
214        Self::Trade(rec)
215    }
216}
217impl<'a> From<&'a TradeMsg> for RecordRefEnum<'a> {
218    fn from(rec: &'a TradeMsg) -> Self {
219        Self::Trade(rec)
220    }
221}
222impl From<Mbp1Msg> for RecordEnum {
223    fn from(rec: Mbp1Msg) -> Self {
224        Self::Mbp1(rec)
225    }
226}
227impl<'a> From<&'a Mbp1Msg> for RecordRefEnum<'a> {
228    fn from(rec: &'a Mbp1Msg) -> Self {
229        Self::Mbp1(rec)
230    }
231}
232impl From<Mbp10Msg> for RecordEnum {
233    fn from(rec: Mbp10Msg) -> Self {
234        Self::Mbp10(rec)
235    }
236}
237impl<'a> From<&'a Mbp10Msg> for RecordRefEnum<'a> {
238    fn from(rec: &'a Mbp10Msg) -> Self {
239        Self::Mbp10(rec)
240    }
241}
242impl From<OhlcvMsg> for RecordEnum {
243    fn from(rec: OhlcvMsg) -> Self {
244        Self::Ohlcv(rec)
245    }
246}
247impl<'a> From<&'a OhlcvMsg> for RecordRefEnum<'a> {
248    fn from(rec: &'a OhlcvMsg) -> Self {
249        Self::Ohlcv(rec)
250    }
251}
252impl From<StatusMsg> for RecordEnum {
253    fn from(rec: StatusMsg) -> Self {
254        Self::Status(rec)
255    }
256}
257impl<'a> From<&'a StatusMsg> for RecordRefEnum<'a> {
258    fn from(rec: &'a StatusMsg) -> Self {
259        Self::Status(rec)
260    }
261}
262impl From<InstrumentDefMsg> for RecordEnum {
263    fn from(rec: InstrumentDefMsg) -> Self {
264        Self::InstrumentDef(rec)
265    }
266}
267impl<'a> From<&'a InstrumentDefMsg> for RecordRefEnum<'a> {
268    fn from(rec: &'a InstrumentDefMsg) -> Self {
269        Self::InstrumentDef(rec)
270    }
271}
272impl From<ImbalanceMsg> for RecordEnum {
273    fn from(rec: ImbalanceMsg) -> Self {
274        Self::Imbalance(rec)
275    }
276}
277impl<'a> From<&'a ImbalanceMsg> for RecordRefEnum<'a> {
278    fn from(rec: &'a ImbalanceMsg) -> Self {
279        Self::Imbalance(rec)
280    }
281}
282impl From<StatMsg> for RecordEnum {
283    fn from(rec: StatMsg) -> Self {
284        Self::Stat(rec)
285    }
286}
287impl<'a> From<&'a StatMsg> for RecordRefEnum<'a> {
288    fn from(rec: &'a StatMsg) -> Self {
289        Self::Stat(rec)
290    }
291}
292impl From<ErrorMsg> for RecordEnum {
293    fn from(rec: ErrorMsg) -> Self {
294        Self::Error(rec)
295    }
296}
297impl<'a> From<&'a ErrorMsg> for RecordRefEnum<'a> {
298    fn from(rec: &'a ErrorMsg) -> Self {
299        Self::Error(rec)
300    }
301}
302impl From<SymbolMappingMsg> for RecordEnum {
303    fn from(rec: SymbolMappingMsg) -> Self {
304        Self::SymbolMapping(rec)
305    }
306}
307impl<'a> From<&'a SymbolMappingMsg> for RecordRefEnum<'a> {
308    fn from(rec: &'a SymbolMappingMsg) -> Self {
309        Self::SymbolMapping(rec)
310    }
311}
312impl From<SystemMsg> for RecordEnum {
313    fn from(rec: SystemMsg) -> Self {
314        Self::System(rec)
315    }
316}
317impl<'a> From<&'a SystemMsg> for RecordRefEnum<'a> {
318    fn from(rec: &'a SystemMsg) -> Self {
319        Self::System(rec)
320    }
321}
322impl From<Cmbp1Msg> for RecordEnum {
323    fn from(rec: Cmbp1Msg) -> Self {
324        Self::Cmbp1(rec)
325    }
326}
327impl<'a> From<&'a Cmbp1Msg> for RecordRefEnum<'a> {
328    fn from(rec: &'a Cmbp1Msg) -> Self {
329        Self::Cmbp1(rec)
330    }
331}
332impl From<CbboMsg> for RecordEnum {
333    fn from(rec: CbboMsg) -> Self {
334        Self::Cbbo(rec)
335    }
336}
337impl<'a> From<&'a CbboMsg> for RecordRefEnum<'a> {
338    fn from(rec: &'a CbboMsg) -> Self {
339        Self::Cbbo(rec)
340    }
341}
342
343impl Record for RecordEnum {
344    fn header(&self) -> &crate::RecordHeader {
345        match self {
346            RecordEnum::Mbo(rec) => rec.header(),
347            RecordEnum::Trade(rec) => rec.header(),
348            RecordEnum::Mbp1(rec) => rec.header(),
349            RecordEnum::Mbp10(rec) => rec.header(),
350            RecordEnum::Ohlcv(rec) => rec.header(),
351            RecordEnum::Status(rec) => rec.header(),
352            RecordEnum::InstrumentDef(rec) => rec.header(),
353            RecordEnum::Imbalance(rec) => rec.header(),
354            RecordEnum::Stat(rec) => rec.header(),
355            RecordEnum::Error(rec) => rec.header(),
356            RecordEnum::SymbolMapping(rec) => rec.header(),
357            RecordEnum::System(rec) => rec.header(),
358            RecordEnum::Cmbp1(rec) => rec.header(),
359            RecordEnum::Bbo(rec) => rec.header(),
360            RecordEnum::Cbbo(rec) => rec.header(),
361        }
362    }
363
364    fn raw_index_ts(&self) -> u64 {
365        match self {
366            RecordEnum::Mbo(rec) => rec.raw_index_ts(),
367            RecordEnum::Trade(rec) => rec.raw_index_ts(),
368            RecordEnum::Mbp1(rec) => rec.raw_index_ts(),
369            RecordEnum::Mbp10(rec) => rec.raw_index_ts(),
370            RecordEnum::Ohlcv(rec) => rec.raw_index_ts(),
371            RecordEnum::Status(rec) => rec.raw_index_ts(),
372            RecordEnum::InstrumentDef(rec) => rec.raw_index_ts(),
373            RecordEnum::Imbalance(rec) => rec.raw_index_ts(),
374            RecordEnum::Stat(rec) => rec.raw_index_ts(),
375            RecordEnum::Error(rec) => rec.raw_index_ts(),
376            RecordEnum::SymbolMapping(rec) => rec.raw_index_ts(),
377            RecordEnum::System(rec) => rec.raw_index_ts(),
378            RecordEnum::Cmbp1(rec) => rec.raw_index_ts(),
379            RecordEnum::Bbo(rec) => rec.raw_index_ts(),
380            RecordEnum::Cbbo(rec) => rec.raw_index_ts(),
381        }
382    }
383}
384
385impl AsRef<[u8]> for RecordEnum {
386    fn as_ref(&self) -> &[u8] {
387        match self {
388            RecordEnum::Mbo(rec) => rec.as_ref(),
389            RecordEnum::Trade(rec) => rec.as_ref(),
390            RecordEnum::Mbp1(rec) => rec.as_ref(),
391            RecordEnum::Mbp10(rec) => rec.as_ref(),
392            RecordEnum::Ohlcv(rec) => rec.as_ref(),
393            RecordEnum::Status(rec) => rec.as_ref(),
394            RecordEnum::InstrumentDef(rec) => rec.as_ref(),
395            RecordEnum::Imbalance(rec) => rec.as_ref(),
396            RecordEnum::Stat(rec) => rec.as_ref(),
397            RecordEnum::Error(rec) => rec.as_ref(),
398            RecordEnum::SymbolMapping(rec) => rec.as_ref(),
399            RecordEnum::System(rec) => rec.as_ref(),
400            RecordEnum::Cmbp1(rec) => rec.as_ref(),
401            RecordEnum::Bbo(rec) => rec.as_ref(),
402            RecordEnum::Cbbo(rec) => rec.as_ref(),
403        }
404    }
405}
406
407impl RecordMut for RecordEnum {
408    fn header_mut(&mut self) -> &mut crate::RecordHeader {
409        match self {
410            RecordEnum::Mbo(rec) => rec.header_mut(),
411            RecordEnum::Trade(rec) => rec.header_mut(),
412            RecordEnum::Mbp1(rec) => rec.header_mut(),
413            RecordEnum::Mbp10(rec) => rec.header_mut(),
414            RecordEnum::Ohlcv(rec) => rec.header_mut(),
415            RecordEnum::Status(rec) => rec.header_mut(),
416            RecordEnum::InstrumentDef(rec) => rec.header_mut(),
417            RecordEnum::Imbalance(rec) => rec.header_mut(),
418            RecordEnum::Stat(rec) => rec.header_mut(),
419            RecordEnum::Error(rec) => rec.header_mut(),
420            RecordEnum::SymbolMapping(rec) => rec.header_mut(),
421            RecordEnum::System(rec) => rec.header_mut(),
422            RecordEnum::Cmbp1(rec) => rec.header_mut(),
423            RecordEnum::Bbo(rec) => rec.header_mut(),
424            RecordEnum::Cbbo(rec) => rec.header_mut(),
425        }
426    }
427}
428
429impl Record for RecordRefEnum<'_> {
430    fn header(&self) -> &crate::RecordHeader {
431        match self {
432            RecordRefEnum::Mbo(rec) => rec.header(),
433            RecordRefEnum::Trade(rec) => rec.header(),
434            RecordRefEnum::Mbp1(rec) => rec.header(),
435            RecordRefEnum::Mbp10(rec) => rec.header(),
436            RecordRefEnum::Ohlcv(rec) => rec.header(),
437            RecordRefEnum::Status(rec) => rec.header(),
438            RecordRefEnum::InstrumentDef(rec) => rec.header(),
439            RecordRefEnum::Imbalance(rec) => rec.header(),
440            RecordRefEnum::Stat(rec) => rec.header(),
441            RecordRefEnum::Error(rec) => rec.header(),
442            RecordRefEnum::SymbolMapping(rec) => rec.header(),
443            RecordRefEnum::System(rec) => rec.header(),
444            RecordRefEnum::Cmbp1(rec) => rec.header(),
445            RecordRefEnum::Bbo(rec) => rec.header(),
446            RecordRefEnum::Cbbo(rec) => rec.header(),
447        }
448    }
449
450    fn raw_index_ts(&self) -> u64 {
451        match self {
452            RecordRefEnum::Mbo(rec) => rec.raw_index_ts(),
453            RecordRefEnum::Trade(rec) => rec.raw_index_ts(),
454            RecordRefEnum::Mbp1(rec) => rec.raw_index_ts(),
455            RecordRefEnum::Mbp10(rec) => rec.raw_index_ts(),
456            RecordRefEnum::Ohlcv(rec) => rec.raw_index_ts(),
457            RecordRefEnum::Status(rec) => rec.raw_index_ts(),
458            RecordRefEnum::InstrumentDef(rec) => rec.raw_index_ts(),
459            RecordRefEnum::Imbalance(rec) => rec.raw_index_ts(),
460            RecordRefEnum::Stat(rec) => rec.raw_index_ts(),
461            RecordRefEnum::Error(rec) => rec.raw_index_ts(),
462            RecordRefEnum::SymbolMapping(rec) => rec.raw_index_ts(),
463            RecordRefEnum::System(rec) => rec.raw_index_ts(),
464            RecordRefEnum::Cmbp1(rec) => rec.raw_index_ts(),
465            RecordRefEnum::Bbo(rec) => rec.raw_index_ts(),
466            RecordRefEnum::Cbbo(rec) => rec.raw_index_ts(),
467        }
468    }
469}
470
471impl AsRef<[u8]> for RecordRefEnum<'_> {
472    fn as_ref(&self) -> &[u8] {
473        match self {
474            RecordRefEnum::Mbo(rec) => rec.as_ref(),
475            RecordRefEnum::Trade(rec) => rec.as_ref(),
476            RecordRefEnum::Mbp1(rec) => rec.as_ref(),
477            RecordRefEnum::Mbp10(rec) => rec.as_ref(),
478            RecordRefEnum::Ohlcv(rec) => rec.as_ref(),
479            RecordRefEnum::Status(rec) => rec.as_ref(),
480            RecordRefEnum::InstrumentDef(rec) => rec.as_ref(),
481            RecordRefEnum::Imbalance(rec) => rec.as_ref(),
482            RecordRefEnum::Stat(rec) => rec.as_ref(),
483            RecordRefEnum::Error(rec) => rec.as_ref(),
484            RecordRefEnum::SymbolMapping(rec) => rec.as_ref(),
485            RecordRefEnum::System(rec) => rec.as_ref(),
486            RecordRefEnum::Cmbp1(rec) => rec.as_ref(),
487            RecordRefEnum::Bbo(rec) => rec.as_ref(),
488            RecordRefEnum::Cbbo(rec) => rec.as_ref(),
489        }
490    }
491}
492
493#[cfg(test)]
494mod tests {
495    use crate::{record::*, v1, v2, HasRType};
496
497    use super::*;
498    use rstest::rstest;
499
500    #[rstest]
501    #[case::mbo(MboMsg::default(), None)]
502    #[case::trade(TradeMsg::default(), None)]
503    #[case::mbp1(Mbp1Msg::default(), None)]
504    #[case::mbp10(Mbp10Msg::default(), None)]
505    #[case::bbo(BboMsg::default_for_schema(crate::Schema::Bbo1S), None)]
506    #[case::cmbp1(Cmbp1Msg::default_for_schema(crate::Schema::Cmbp1), None)]
507    #[case::cbbo(CbboMsg::default_for_schema(crate::Schema::Cbbo1S), None)]
508    #[case::ohlcv(OhlcvMsg::default_for_schema(crate::Schema::Ohlcv1S), None)]
509    #[case::status(StatusMsg::default(), None)]
510    #[case::imbalance(ImbalanceMsg::default(), None)]
511    #[case::instrument_def_v1(
512        v1::InstrumentDefMsg::default(),
513        Some("couldn't convert earlier version of InstrumentDefMsg (must be current version) to dbn::record_enum::RecordRefEnum")
514    )]
515    #[case::instrument_def_v2(
516        v2::InstrumentDefMsg::default(),
517        Some("couldn't convert earlier version of InstrumentDefMsg (must be current version) to dbn::record_enum::RecordRefEnum")
518    )]
519    #[case::instrument_def_current(InstrumentDefMsg::default(), None)]
520    #[case::symbol_mapping_v1(
521        v1::SymbolMappingMsg::default(),
522        Some("couldn't convert earlier version of SymbolMappingMsg (must be current version) to dbn::record_enum::RecordRefEnum")
523    )]
524    #[case::symbol_mapping_current(SymbolMappingMsg::default(), None)]
525    #[case::system_v1(
526        v1::SystemMsg::default(),
527        Some("couldn't convert earlier version of SystemMsg (must be current version) to dbn::record_enum::RecordRefEnum")
528    )]
529    #[case::system_current(SystemMsg::default(), None)]
530    #[case::error_v1(
531        v1::ErrorMsg::default(),
532        Some("couldn't convert earlier version of ErrorMsg (must be current version) to dbn::record_enum::RecordRefEnum")
533    )]
534    #[case::error_current(ErrorMsg::default(), None)]
535    #[case::stat_v1(
536        v1::StatMsg::default(),
537        Some("couldn't convert earlier version of StatMsg (must be current version) to dbn::record_enum::RecordRefEnum")
538    )]
539    #[case::stat_current(StatMsg::default(), None)]
540    fn test_v1_v2_safety<R: HasRType>(#[case] rec: R, #[case] exp_err: Option<&str>) {
541        let rec_ref = RecordRef::from(&rec);
542        let res = rec_ref.as_enum();
543        dbg!(&res);
544        if let Some(exp_err) = exp_err {
545            assert!(format!("{}", res.unwrap_err()).contains(exp_err));
546        } else {
547            assert!(res.is_ok());
548        }
549    }
550}