sawp_flags/
lib.rs

1//! Bitflags handling and storage.
2//!
3//! This crate allows you to define flag values using an enum and derive
4//! `BitFlags` to add convenience methods.
5//!
6//! This implementation was heavily inspired by
7//! [enumflags2](https://crates.io/crates/enumflags2) and
8//! [bitflags](https://crates.io/crates/bitflags) and customized for use in a
9//! sawp parser. Consider using those two open source projects before resorting
10//! to this one. One key feature is that we are automatically generating ffi
11//! accessors using the [sawp-ffi](https://crates.io/crates/sawp-ffi) crate.
12//!
13//! This crate works as follows:
14//! - `enum YourEnum` with a numeric representation (e.g. `#[repr(u8)]`) is used
15//!   to define bit fields.
16//! - deriving `BitFlags` on this enum will add convenience methods for bitwise
17//!   operations and implement the `Flag` trait.
18//! - Flag values are transparently stored as `Flags<YourEnum>` so you can perform
19//!   more operations on this type.
20//!
21//! # Example
22//! See `example` module for a generated example as well.
23//! ```
24//! use sawp_flags::{BitFlags, Flags, Flag};
25//!
26//! /// Example enum
27//! #[derive(Debug, Clone, Copy, PartialEq, BitFlags)]
28//! #[repr(u8)]
29//! pub enum Test {
30//!     A = 0b0001,
31//!     B = 0b0010,
32//!     C = 0b0100,
33//!     D = 0b1000,
34//!     /// Variants can be a bitmask of the other fields like so
35//!     E = Test::A as u8 | Test::B as u8 | Test::C as u8 | Test::D as u8,
36//! }
37//!
38//! // `flags` will be of transparent type `Flags<Test>`
39//! let flags : Flags<Test> = Test::A | Test::C;
40//!
41//! // convert a number to flags using `from_bits()`
42//! assert_eq!(flags, Flags::<Test>::from_bits(0b101));
43//!
44//! // convert flags to a number using `bits()`
45//! assert_eq!(0b101, flags.bits());
46//!
47//! // perform bitwise operations
48//! assert_eq!(Test::A | Test::B | Test::C, flags | Test::B);
49//! assert_eq!(Test::A, flags & Test::A);
50//! assert_eq!(Test::C, flags ^ Test::A);
51//!
52//! // check which flags are set
53//! assert!(flags.contains(Test::A));
54//! assert!(!flags.contains(Test::A | Test::B));
55//! assert!(flags.intersects(Test::A));
56//! assert!(flags.intersects(Test::A | Test::B));
57//! ```
58
59use std::ops::*;
60
61/// The `BitFlags` derive macro will implement the `Flags` Trait on your enum and
62/// provide convenience methods for bit operations and type conversions.
63///
64// Re-export derive macro for convenience.
65pub use sawp_flags_derive::BitFlags;
66
67/// A primitive numeric type to be used for flag storage.
68pub trait Primitive:
69    Default
70    + BitOr<Self, Output = Self>
71    + BitAnd<Self, Output = Self>
72    + BitXor<Self, Output = Self>
73    + Not<Output = Self>
74    + PartialOrd<Self>
75    + std::fmt::Debug
76    + std::fmt::Binary
77    + Copy
78    + Clone
79{
80}
81
82impl Primitive for u8 {}
83impl Primitive for u16 {}
84impl Primitive for u32 {}
85impl Primitive for u64 {}
86impl Primitive for u128 {}
87
88/// A trait implemented by all flag enums.
89pub trait Flag: Copy + Clone + std::fmt::Debug + std::fmt::Display + 'static {
90    /// Associated primitive numeric type
91    type Primitive: Primitive;
92
93    /// A list of all flag variants in the enum
94    const ITEMS: &'static [Self];
95
96    /// Numeric representation of the variant
97    fn bits(self) -> Self::Primitive;
98
99    /// Flag value when no variants are set
100    fn none() -> Flags<Self>;
101
102    /// Flag value when all variants are set
103    fn all() -> Flags<Self>;
104}
105
106/// Storage type for handling flags
107#[derive(Copy, Clone, PartialEq, Eq)]
108#[repr(transparent)]
109pub struct Flags<Enum, Primitive = <Enum as Flag>::Primitive> {
110    val: Primitive,
111    marker: std::marker::PhantomData<Enum>,
112}
113
114impl<Enum> std::fmt::Debug for Flags<Enum>
115where
116    Enum: Flag,
117{
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        self.val.fmt(f)
120    }
121}
122
123impl<Enum> Default for Flags<Enum>
124where
125    Enum: Flag,
126{
127    fn default() -> Self {
128        Self {
129            val: <Enum as Flag>::Primitive::default(),
130            marker: std::marker::PhantomData,
131        }
132    }
133}
134
135impl<Enum> Flags<Enum>
136where
137    Enum: Flag,
138{
139    /// Get a flag from a single enum value
140    pub fn from_flag(flag: Enum) -> Self {
141        Self {
142            val: flag.bits(),
143            marker: std::marker::PhantomData,
144        }
145    }
146
147    /// Get a flag from a numeric value
148    ///
149    /// Note: the value is unchecked so any bit may be set. Be
150    /// careful because `PartialEq` is a direct comparison of
151    /// underlying bits.
152    pub fn from_bits(bits: <Enum as Flag>::Primitive) -> Self {
153        Self {
154            val: bits,
155            marker: std::marker::PhantomData,
156        }
157    }
158
159    /// Numeric representation of the variant
160    pub fn bits(&self) -> <Enum as Flag>::Primitive {
161        self.val
162    }
163
164    /// Reference to numeric representation of the variant
165    pub fn bits_ref(&self) -> &<Enum as Flag>::Primitive {
166        &self.val
167    }
168
169    /// Check if at least one flag in common is set
170    pub fn intersects<B: Into<Flags<Enum>>>(self, rhs: B) -> bool {
171        (self & rhs.into()).bits() != Enum::none().bits()
172    }
173
174    /// Check if all flags provided in `rhs` are set
175    pub fn contains<B: Into<Flags<Enum>>>(self, rhs: B) -> bool {
176        let rhs = rhs.into();
177        (self & rhs).bits() == rhs.bits()
178    }
179
180    pub fn is_empty(&self) -> bool {
181        self.bits() == <Enum as Flag>::none().bits()
182    }
183
184    pub fn is_all(&self) -> bool {
185        self.bits() == <Enum as Flag>::all().bits()
186    }
187}
188
189impl<Enum: Flag> From<Enum> for Flags<Enum> {
190    fn from(flag: Enum) -> Self {
191        Self::from_flag(flag)
192    }
193}
194
195impl<Enum: Flag> PartialEq<Enum> for Flags<Enum> {
196    fn eq(&self, other: &Enum) -> bool {
197        self.bits() == other.bits()
198    }
199}
200
201impl<T, B> std::ops::BitOr<B> for Flags<T>
202where
203    T: Flag,
204    B: Into<Flags<T>>,
205{
206    type Output = Flags<T>;
207    fn bitor(self, other: B) -> Flags<T> {
208        Flags::from_bits(self.bits() | other.into().bits())
209    }
210}
211
212impl<T, B> std::ops::BitOrAssign<B> for Flags<T>
213where
214    T: Flag,
215    B: Into<Flags<T>>,
216{
217    fn bitor_assign(&mut self, rhs: B) {
218        *self = Flags::from_bits(self.bits() | rhs.into().bits())
219    }
220}
221
222impl<T, B> std::ops::BitAnd<B> for Flags<T>
223where
224    T: Flag,
225    B: Into<Flags<T>>,
226{
227    type Output = Flags<T>;
228    fn bitand(self, other: B) -> Flags<T> {
229        Flags::from_bits(self.bits() & other.into().bits())
230    }
231}
232
233impl<T, B> std::ops::BitAndAssign<B> for Flags<T>
234where
235    T: Flag,
236    B: Into<Flags<T>>,
237{
238    fn bitand_assign(&mut self, rhs: B) {
239        *self = Flags::from_bits(self.bits() & rhs.into().bits())
240    }
241}
242
243impl<T, B> std::ops::BitXor<B> for Flags<T>
244where
245    T: Flag,
246    B: Into<Flags<T>>,
247{
248    type Output = Flags<T>;
249    fn bitxor(self, other: B) -> Flags<T> {
250        Flags::from_bits(self.bits() ^ other.into().bits())
251    }
252}
253
254impl<T, B> std::ops::BitXorAssign<B> for Flags<T>
255where
256    T: Flag,
257    B: Into<Flags<T>>,
258{
259    fn bitxor_assign(&mut self, rhs: B) {
260        *self = Flags::from_bits(self.bits() ^ rhs.into().bits())
261    }
262}
263
264impl<T: Flag> std::ops::Not for Flags<T> {
265    type Output = Flags<T>;
266
267    fn not(self) -> Self::Output {
268        Flags::from_bits(!self.bits())
269    }
270}
271
272impl<T: Flag> std::fmt::Display for Flags<T> {
273    /// A pipe-separated list of set flags.
274    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275        let none = self.bits() == T::none().bits();
276        let mut first = true;
277        for val in <T as Flag>::ITEMS
278            .iter()
279            .cloned()
280            .filter(move |&flag| self.contains(flag))
281        {
282            write!(f, "{}{:?}", if first { "" } else { " | " }, val)?;
283            first = false;
284
285            if none {
286                return Ok(());
287            }
288        }
289
290        if none {
291            write!(f, "NONE")?;
292        }
293
294        Ok(())
295    }
296}
297
298impl<T: Flag> std::fmt::Binary for Flags<T> {
299    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
300        std::fmt::Binary::fmt(&self.bits(), f)
301    }
302}
303
304/// Example enum deriving `BitFlags`
305pub mod example {
306    use super::*;
307
308    /// Example enum
309    #[derive(Debug, Clone, Copy, PartialEq, Eq, BitFlags)]
310    #[repr(u8)]
311    pub enum Test {
312        A = 0b0001,
313        B = 0b0010,
314        C = 0b0100,
315        D = 0b1000,
316        /// Variants can be bitmask of other fields
317        E = Test::A as u8 | Test::B as u8 | Test::C as u8 | Test::D as u8,
318    }
319}
320
321#[cfg(test)]
322mod test {
323    use super::{example::*, *};
324
325    #[test]
326    fn test_enum_bits() {
327        let bits = 0b1010_1010;
328        let flags = Flags::<Test>::from_bits(bits);
329        assert_eq!(bits, flags.bits());
330        assert_eq!(&bits, flags.bits_ref());
331    }
332
333    #[test]
334    fn test_enum_or() {
335        let mut flags = Test::A | Test::B;
336        assert_eq!(0b0011, flags.bits());
337
338        flags |= Test::C;
339        assert_eq!(0b0111, flags.bits());
340
341        flags |= Test::C | Test::D;
342        assert_eq!(0b1111, flags.bits());
343    }
344
345    #[test]
346    fn test_enum_and() {
347        let mut flags = Test::E & Test::B;
348        assert_eq!(0b0010, flags.bits());
349
350        flags &= Test::B;
351        assert_eq!(0b0010, flags.bits());
352
353        flags &= Test::E & Test::B;
354        assert_eq!(0b0010, flags.bits());
355    }
356
357    #[test]
358    fn test_enum_xor() {
359        let mut flags = Test::A ^ Test::B;
360        assert_eq!(0b0011, flags.bits());
361
362        flags ^= Test::C;
363        assert_eq!(0b0111, flags.bits());
364
365        flags ^= Test::D ^ Test::B;
366        assert_eq!(0b1101, flags.bits());
367    }
368
369    #[test]
370    fn test_enum_not() {
371        let flags = !Test::A;
372        assert_eq!(0b1111_1110, flags.bits());
373        let flags = !(Test::A ^ Test::B);
374        assert_eq!(0b1111_1100, flags.bits());
375    }
376
377    #[test]
378    fn test_contains() {
379        let flags = Test::A | Test::C;
380        assert!(flags.contains(Test::A));
381        assert!(!flags.contains(Test::B));
382        assert!(!flags.contains(Test::E));
383        assert!(!flags.contains(Test::B | Test::D));
384        assert!(!flags.contains(Test::A | Test::B));
385        assert!(flags.contains(Test::A | Test::C));
386    }
387
388    #[test]
389    fn test_intersects() {
390        let flags = Test::A | Test::C;
391        assert!(flags.intersects(Test::A));
392        assert!(flags.intersects(Test::E));
393        assert!(flags.intersects(Test::A | Test::B));
394        assert!(flags.intersects(Test::A | Test::C));
395        assert!(flags.intersects(Test::A | Test::B | Test::C));
396        assert!(!flags.intersects(Test::B | Test::D));
397    }
398
399    #[test]
400    fn test_eq() {
401        let flags = Test::A;
402        assert_eq!(flags, Test::A);
403        assert_eq!(Test::A, flags);
404
405        let flags = Test::A | Test::C;
406        assert_ne!(flags, Test::A);
407        assert_ne!(flags, Test::C);
408        assert_ne!(Test::A, flags);
409        assert_eq!(flags, Test::A | Test::C);
410        assert_ne!(flags, Test::A | Test::C | Test::E);
411
412        let flags = Flags::<Test>::from_bits(0b1000_0001);
413        assert_ne!(flags, Test::A);
414    }
415
416    #[test]
417    fn test_enum_string() {
418        assert_eq!("NONE", Test::none().to_string());
419        assert_eq!("A", Test::A.to_string());
420        assert_eq!("A | B", (Test::A | Test::B).to_string());
421        assert_eq!("A | B | C | D | E", Test::E.to_string());
422        assert_eq!("A | B | C | D | E", Flags::from_flag(Test::E).to_string());
423    }
424
425    #[test]
426    fn test_enum_string_none() {
427        #[derive(Debug, Clone, Copy, PartialEq, Eq, BitFlags)]
428        #[repr(u8)]
429        pub enum Test {
430            Zero = 0b0000,
431            A = 0b0001,
432            B = 0b0010,
433            C = 0b0100,
434            D = 0b1000,
435            /// Variants can be bitmask of other fields
436            E = Test::A as u8 | Test::B as u8 | Test::C as u8 | Test::D as u8,
437        }
438        assert_eq!("Zero", Test::Zero.to_string());
439        assert_eq!("Zero", Test::none().to_string());
440        assert_eq!("Zero", Flags::from_flag(Test::Zero).to_string());
441    }
442
443    #[test]
444    fn test_enum_format() {
445        assert_eq!("A", format!("{:?}", Test::A));
446        assert_eq!("E", format!("{:?}", Test::E));
447        assert_eq!("0", format!("{:?}", Test::none()));
448
449        assert_eq!("0", format!("{:b}", Test::none()));
450        assert_eq!("1", format!("{:b}", Test::A));
451        assert_eq!("1111", format!("{:b}", Test::E));
452    }
453
454    #[test]
455    fn test_enum_from_str() {
456        use std::str::FromStr;
457        assert_eq!(Err(()), Test::from_str(""));
458        assert_eq!(Ok(Test::A), Test::from_str("a"));
459        assert_eq!(Ok(Test::A), Test::from_str("A"));
460    }
461
462    #[test]
463    fn test_all() {
464        assert_eq!(Test::E, Test::all());
465        assert!(!Flags::from_flag(Test::A).is_all());
466        assert!(Flags::from_flag(Test::E).is_all());
467    }
468
469    #[test]
470    fn test_none() {
471        assert_eq!(Flags::from_bits(0), Test::none());
472        assert!(Flags::<Test>::from_bits(0).is_empty());
473        assert!(!Flags::from_flag(Test::A).is_empty());
474        assert!(!Flags::from_flag(Test::E).is_empty());
475    }
476}