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)]
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 pub const fn empty() -> Self {
77 Self { raw: 0 }
78 }
79
80 pub const fn new(raw: u8) -> Self {
82 Self { raw }
83 }
84
85 pub fn clear(&mut self) -> &mut Self {
87 self.raw = 0;
88 self
89 }
90
91 pub const fn raw(&self) -> u8 {
93 self.raw
94 }
95
96 pub fn set_raw(&mut self, raw: u8) {
98 self.raw = raw;
99 }
100
101 pub const fn any(&self) -> bool {
103 self.raw > 0
104 }
105
106 pub fn is_empty(&self) -> bool {
108 self.raw == 0
109 }
110
111 pub const fn is_last(&self) -> bool {
114 (self.raw & LAST) > 0
115 }
116
117 pub fn set_last(&mut self) -> Self {
120 self.raw |= LAST;
121 *self
122 }
123
124 pub const fn is_tob(&self) -> bool {
126 (self.raw & TOB) > 0
127 }
128
129 pub fn set_tob(&mut self) -> Self {
131 self.raw |= TOB;
132 *self
133 }
134
135 pub const fn is_snapshot(&self) -> bool {
138 (self.raw & SNAPSHOT) > 0
139 }
140
141 pub fn set_snapshot(&mut self) -> Self {
144 self.raw |= SNAPSHOT;
145 *self
146 }
147
148 pub const fn is_mbp(&self) -> bool {
151 (self.raw & MBP) > 0
152 }
153
154 pub fn set_mbp(&mut self) -> Self {
157 self.raw |= MBP;
158 *self
159 }
160
161 pub const fn is_bad_ts_recv(&self) -> bool {
164 (self.raw & BAD_TS_RECV) > 0
165 }
166
167 pub fn set_bad_ts_recv(&mut self) -> Self {
170 self.raw |= BAD_TS_RECV;
171 *self
172 }
173
174 pub const fn is_maybe_bad_book(&self) -> bool {
177 (self.raw & MAYBE_BAD_BOOK) > 0
178 }
179
180 pub fn set_maybe_bad_book(&mut self) -> Self {
183 self.raw |= MAYBE_BAD_BOOK;
184 *self
185 }
186
187 pub const fn is_publisher_specific(&self) -> bool {
189 (self.raw & PUBLISHER_SPECIFIC) > 0
190 }
191
192 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}