1use std::fmt;
4
5#[cfg(feature = "python")]
6use pyo3::prelude::*;
7
8pub const LAST: u8 = 1 << 7;
11pub const TOB: u8 = 1 << 6;
13pub const SNAPSHOT: u8 = 1 << 5;
15pub const MBP: u8 = 1 << 4;
17pub const BAD_TS_RECV: u8 = 1 << 3;
20pub const MAYBE_BAD_BOOK: u8 = 1 << 2;
22pub const PUBLISHER_SPECIFIC: u8 = 1 << 1;
24
25#[repr(transparent)]
35#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
36#[cfg_attr(feature = "python", derive(FromPyObject), pyo3(transparent))]
37#[cfg_attr(
38 feature = "serde",
39 derive(serde::Serialize, serde::Deserialize),
40 serde(transparent)
41)]
42pub struct FlagSet {
43 raw: u8,
44}
45
46impl fmt::Debug for FlagSet {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 let mut has_written_flag = false;
49 for (flag, name) in [
50 (LAST, stringify!(LAST)),
51 (TOB, stringify!(TOB)),
52 (SNAPSHOT, stringify!(SNAPSHOT)),
53 (MBP, stringify!(MBP)),
54 (BAD_TS_RECV, stringify!(BAD_TS_RECV)),
55 (MAYBE_BAD_BOOK, stringify!(MAYBE_BAD_BOOK)),
56 (PUBLISHER_SPECIFIC, stringify!(PUBLISHER_SPECIFIC)),
57 ] {
58 if (self.raw() & flag) > 0 {
59 if has_written_flag {
60 write!(f, " | {name}")?;
61 } else {
62 write!(f, "{name}")?;
63 has_written_flag = true;
64 }
65 }
66 }
67 if has_written_flag {
68 write!(f, " ({})", self.raw())
69 } else {
70 write!(f, "{}", self.raw())
71 }
72 }
73}
74
75impl From<u8> for FlagSet {
76 fn from(raw: u8) -> Self {
77 Self { raw }
78 }
79}
80
81impl FlagSet {
82 pub const fn empty() -> Self {
84 Self { raw: 0 }
85 }
86
87 pub const fn new(raw: u8) -> Self {
89 Self { raw }
90 }
91
92 pub const fn clear(&mut self) -> &mut Self {
94 self.raw = 0;
95 self
96 }
97
98 pub const fn raw(&self) -> u8 {
100 self.raw
101 }
102
103 pub const fn set_raw(&mut self, raw: u8) {
105 self.raw = raw;
106 }
107
108 pub const fn any(&self) -> bool {
110 self.raw > 0
111 }
112
113 pub const fn is_empty(&self) -> bool {
115 self.raw == 0
116 }
117
118 pub const fn is_last(&self) -> bool {
121 (self.raw & LAST) > 0
122 }
123
124 pub const fn set_last(&mut self) -> Self {
127 self.raw |= LAST;
128 *self
129 }
130
131 pub const fn is_tob(&self) -> bool {
133 (self.raw & TOB) > 0
134 }
135
136 pub const fn set_tob(&mut self) -> Self {
138 self.raw |= TOB;
139 *self
140 }
141
142 pub const fn is_snapshot(&self) -> bool {
145 (self.raw & SNAPSHOT) > 0
146 }
147
148 pub const fn set_snapshot(&mut self) -> Self {
151 self.raw |= SNAPSHOT;
152 *self
153 }
154
155 pub const fn is_mbp(&self) -> bool {
158 (self.raw & MBP) > 0
159 }
160
161 pub const fn set_mbp(&mut self) -> Self {
164 self.raw |= MBP;
165 *self
166 }
167
168 pub const fn is_bad_ts_recv(&self) -> bool {
171 (self.raw & BAD_TS_RECV) > 0
172 }
173
174 pub const fn set_bad_ts_recv(&mut self) -> Self {
177 self.raw |= BAD_TS_RECV;
178 *self
179 }
180
181 pub const fn is_maybe_bad_book(&self) -> bool {
184 (self.raw & MAYBE_BAD_BOOK) > 0
185 }
186
187 pub const fn set_maybe_bad_book(&mut self) -> Self {
190 self.raw |= MAYBE_BAD_BOOK;
191 *self
192 }
193
194 pub const fn is_publisher_specific(&self) -> bool {
196 (self.raw & PUBLISHER_SPECIFIC) > 0
197 }
198
199 pub const fn set_publisher_specific(&mut self) -> Self {
201 self.raw |= PUBLISHER_SPECIFIC;
202 *self
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 use rstest::*;
211
212 #[rstest]
213 #[case::empty(FlagSet::empty(), "0")]
214 #[case::one_set(FlagSet::empty().set_mbp(), "MBP (16)")]
215 #[case::three_set(FlagSet::empty().set_tob().set_snapshot().set_maybe_bad_book(), "TOB | SNAPSHOT | MAYBE_BAD_BOOK (100)")]
216 #[case::reserved_set(
217 FlagSet::new(255),
218 "LAST | TOB | SNAPSHOT | MBP | BAD_TS_RECV | MAYBE_BAD_BOOK | PUBLISHER_SPECIFIC (255)"
219 )]
220 fn dbg(#[case] target: FlagSet, #[case] exp: &str) {
221 assert_eq!(format!("{target:?}"), exp);
222 }
223}