Skip to main content

dbn/
flags.rs

1//! Bit set flags used in Databento market data.
2
3use std::fmt;
4
5#[cfg(feature = "python")]
6use pyo3::prelude::*;
7
8/// Indicates it's the last record in the event from the venue for a given
9/// `instrument_id`.
10pub const LAST: u8 = 1 << 7;
11/// Indicates a top-of-book record, not an individual order.
12pub const TOB: u8 = 1 << 6;
13/// Indicates the record was sourced from a replay, such as a snapshot server.
14pub const SNAPSHOT: u8 = 1 << 5;
15/// Indicates an aggregated price level record, not an individual order.
16pub const MBP: u8 = 1 << 4;
17/// Indicates the `ts_recv` value is inaccurate due to clock issues or packet
18/// reordering.
19pub const BAD_TS_RECV: u8 = 1 << 3;
20/// Indicates an unrecoverable gap was detected in the channel.
21pub const MAYBE_BAD_BOOK: u8 = 1 << 2;
22/// Used to indicate a publisher-specific event.
23pub const PUBLISHER_SPECIFIC: u8 = 1 << 1;
24
25/// A transparent wrapper around the bit field used in several DBN record types,
26/// namely [`MboMsg`](crate::MboMsg) and record types derived from it.
27#[repr(transparent)]
28#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
29#[cfg_attr(feature = "python", derive(FromPyObject), pyo3(transparent))]
30#[cfg_attr(
31    feature = "serde",
32    derive(serde::Serialize, serde::Deserialize),
33    serde(transparent)
34)]
35pub struct FlagSet {
36    raw: u8,
37}
38
39impl fmt::Debug for FlagSet {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        let mut has_written_flag = false;
42        for (flag, name) in [
43            (LAST, stringify!(LAST)),
44            (TOB, stringify!(TOB)),
45            (SNAPSHOT, stringify!(SNAPSHOT)),
46            (MBP, stringify!(MBP)),
47            (BAD_TS_RECV, stringify!(BAD_TS_RECV)),
48            (MAYBE_BAD_BOOK, stringify!(MAYBE_BAD_BOOK)),
49            (PUBLISHER_SPECIFIC, stringify!(PUBLISHER_SPECIFIC)),
50        ] {
51            if (self.raw() & flag) > 0 {
52                if has_written_flag {
53                    write!(f, " | {name}")?;
54                } else {
55                    write!(f, "{name}")?;
56                    has_written_flag = true;
57                }
58            }
59        }
60        if has_written_flag {
61            write!(f, " ({})", self.raw())
62        } else {
63            write!(f, "{}", self.raw())
64        }
65    }
66}
67
68impl From<u8> for FlagSet {
69    fn from(raw: u8) -> Self {
70        Self { raw }
71    }
72}
73
74impl FlagSet {
75    /// Returns an empty [`FlagSet`]: one with no flags set.
76    pub const fn empty() -> Self {
77        Self { raw: 0 }
78    }
79
80    /// Creates a new flag set from `raw`.
81    pub const fn new(raw: u8) -> Self {
82        Self { raw }
83    }
84
85    /// Turns all flags off, i.e. to `false`.
86    pub fn clear(&mut self) -> &mut Self {
87        self.raw = 0;
88        self
89    }
90
91    /// Returns the raw value.
92    pub const fn raw(&self) -> u8 {
93        self.raw
94    }
95
96    /// Sets the flags directly with a raw `u8`.
97    pub fn set_raw(&mut self, raw: u8) {
98        self.raw = raw;
99    }
100
101    /// Returns `true` if any of the flags are on or set to true.
102    pub const fn any(&self) -> bool {
103        self.raw > 0
104    }
105
106    /// Returns `true` if all flags are unset/false.
107    pub fn is_empty(&self) -> bool {
108        self.raw == 0
109    }
110
111    /// Returns `true` if it's the last record in the event from the venue for a given
112    /// `instrument_id`.
113    pub const fn is_last(&self) -> bool {
114        (self.raw & LAST) > 0
115    }
116
117    /// Sets the `LAST` bit flag to `true` to indicate this is the last record in the
118    /// event for a given instrument.
119    pub fn set_last(&mut self) -> Self {
120        self.raw |= LAST;
121        *self
122    }
123
124    /// Returns `true` if it's a top-of-book record, not an individual order.
125    pub const fn is_tob(&self) -> bool {
126        (self.raw & TOB) > 0
127    }
128
129    /// Sets the `TOB` bit flag to `true` to indicate this is a top-of-book record.
130    pub fn set_tob(&mut self) -> Self {
131        self.raw |= TOB;
132        *self
133    }
134
135    /// Returns `true` if this record was sourced from a replay, such as a snapshot
136    /// server.
137    pub const fn is_snapshot(&self) -> bool {
138        (self.raw & SNAPSHOT) > 0
139    }
140
141    /// Sets the `SNAPSHOT` bit flag to `true` to indicate this record was sourced from
142    /// a replay.
143    pub fn set_snapshot(&mut self) -> Self {
144        self.raw |= SNAPSHOT;
145        *self
146    }
147
148    /// Returns `true` if this record is an aggregated price level record, not an
149    /// individual order.
150    pub const fn is_mbp(&self) -> bool {
151        (self.raw & MBP) > 0
152    }
153
154    /// Sets the `MBP` bit flag to `true` to indicate this record is an aggregated price
155    /// level record.
156    pub fn set_mbp(&mut self) -> Self {
157        self.raw |= MBP;
158        *self
159    }
160
161    /// Returns `true` if this record has an inaccurate `ts_recv` value due to clock
162    /// issues or packet reordering.
163    pub const fn is_bad_ts_recv(&self) -> bool {
164        (self.raw & BAD_TS_RECV) > 0
165    }
166
167    /// Sets the `BAD_TS_RECV` bit flag to `true` to indicate this record has an
168    /// inaccurate `ts_recv` value.
169    pub fn set_bad_ts_recv(&mut self) -> Self {
170        self.raw |= BAD_TS_RECV;
171        *self
172    }
173
174    /// Returns `true` if this record is from a channel where an unrecoverable gap was
175    /// detected.
176    pub const fn is_maybe_bad_book(&self) -> bool {
177        (self.raw & MAYBE_BAD_BOOK) > 0
178    }
179
180    /// Sets the `MAYBE_BAD_BOOK` bit flag to `true` to indicate this record is from a
181    /// channel where an unrecoverable gap was detected.
182    pub fn set_maybe_bad_book(&mut self) -> Self {
183        self.raw |= MAYBE_BAD_BOOK;
184        *self
185    }
186
187    /// Returns `true` if this record has the publisher-specific flag set.
188    pub const fn is_publisher_specific(&self) -> bool {
189        (self.raw & PUBLISHER_SPECIFIC) > 0
190    }
191
192    /// Sets the `PUBLISHER_SPECIFIC` bit flag to `true`.
193    pub fn set_publisher_specific(&mut self) -> Self {
194        self.raw |= PUBLISHER_SPECIFIC;
195        *self
196    }
197}
198
199#[cfg(test)]
200mod tests {
201    use super::*;
202
203    use rstest::*;
204
205    #[rstest]
206    #[case::empty(FlagSet::empty(), "0")]
207    #[case::one_set(FlagSet::empty().set_mbp(), "MBP (16)")]
208    #[case::three_set(FlagSet::empty().set_tob().set_snapshot().set_maybe_bad_book(), "TOB | SNAPSHOT | MAYBE_BAD_BOOK (100)")]
209    #[case::reserved_set(
210        FlagSet::new(255),
211        "LAST | TOB | SNAPSHOT | MBP | BAD_TS_RECV | MAYBE_BAD_BOOK | PUBLISHER_SPECIFIC (255)"
212    )]
213    fn dbg(#[case] target: FlagSet, #[case] exp: &str) {
214        assert_eq!(format!("{target:?}"), exp);
215    }
216}