Skip to main content

bitflags_lite/
spec.rs

1/// Associates a bit-storage type `T` with the set of bits that are valid for a
2/// particular [`BitFlags`] tag.
3///
4/// Implement this trait for the tag type `U` in [`BitFlags<T, U>`].
5/// [`VALID_BITS_MASK`](Self::VALID_BITS_MASK) defines which bits are valid for
6/// the flag set.
7///
8/// Bits not set in `VALID_BITS_MASK` are invalid:
9/// - [`BitFlags::try_from_bits`] rejects them.
10/// - [`BitFlags::from_bits_truncate`] and [`BitFlags::truncate`] clear them.
11/// - [`BitFlags::complement`] and [`Not`](core::ops::Not) clear them.
12/// - [`BitFlags::from_bits_retain`] and operations that do not explicitly
13///   truncate may preserve them.
14///
15/// Unlike the `bitflags` crate, valid bits are not declared by a macro. They
16/// are part of this trait implementation for the tag type.
17///
18/// # Examples
19///
20/// ```
21/// use bitflags_lite::{BitFlags, BitFlagsSpec, NameMatcher};
22///
23/// struct PermissionsTag;
24///
25/// impl BitFlagsSpec<u32> for PermissionsTag {
26///     const VALID_BITS_MASK: u32 = 0b111;
27///     const FLAG_NAMES: &[&[&str]] = &[&["read"], &["write"], &["exec", "execute"]];
28///     const COMBINED_NAMES: &[(u32, &str)] = &[(0, "none"), (0b111, "rwx")];
29///     const DEFAULT_NAME_MATCHER: NameMatcher = NameMatcher::Exact;
30/// }
31///
32/// type Permissions = BitFlags<u32, PermissionsTag>;
33///
34/// const READ: Permissions = Permissions::try_from_bit(0).unwrap();
35/// const WRITE: Permissions = Permissions::try_from_bit(1).unwrap();
36/// const EXEC: Permissions = Permissions::try_from_bit(2).unwrap();
37///
38/// assert_eq!(Permissions::try_from_bits(0b1), Some(READ));
39/// assert_eq!(Permissions::try_from_bits(0b11), Some(READ | WRITE));
40/// assert_eq!(Permissions::try_from_bits(0b111), Some(READ | WRITE | EXEC));
41/// assert_eq!(Permissions::try_from_bits(0b1111), None);
42/// ```
43///
44/// [`BitFlags`]: struct.BitFlags.html
45/// [`BitFlags<T, U>`]: struct.BitFlags.html
46/// [`BitFlags::try_from_bits`]: struct.BitFlags.html#method.try_from_bits
47/// [`BitFlags::from_bits_truncate`]: struct.BitFlags.html#method.from_bits_truncate
48/// [`BitFlags::truncate`]: struct.BitFlags.html#method.truncate
49/// [`BitFlags::complement`]: struct.BitFlags.html#method.complement
50/// [`BitFlags::from_bits_retain`]: struct.BitFlags.html#method.from_bits_retain
51pub trait BitFlagsSpec<T>
52where
53    T: 'static,
54{
55    /// A bit pattern whose set bits are the valid bits for this flag set.
56    ///
57    /// Bits not set in this value are invalid for the associated [`BitFlags`]
58    /// type.
59    /// They are rejected by [`BitFlags::try_from_bits`] and cleared by
60    /// [`BitFlags::from_bits_truncate`], [`BitFlags::truncate`],
61    /// [`BitFlags::complement`], and [`Not`](core::ops::Not).
62    ///
63    /// [`BitFlags`]: struct.BitFlags.html
64    /// [`BitFlags::try_from_bits`]: struct.BitFlags.html#method.try_from_bits
65    /// [`BitFlags::from_bits_truncate`]: struct.BitFlags.html#method.from_bits_truncate
66    /// [`BitFlags::truncate`]: struct.BitFlags.html#method.truncate
67    /// [`BitFlags::complement`]: struct.BitFlags.html#method.complement
68    const VALID_BITS_MASK: T;
69
70    /// A slice indexed by bit position, where each element contains the names
71    /// associated with that bit.
72    ///
73    /// The first name in each inner slice is used by [`BitFlag`]'s and
74    /// [`BitFlags`]'s [`Display`](core::fmt::Display) implementations.
75    /// All names are accepted when parsing.
76    /// An empty inner slice means the bit has no name.
77    ///
78    /// The index `i` corresponds to the bit mask `1 << i`.
79    ///
80    /// [`BitFlag`]: struct.BitFlag.html
81    /// [`BitFlags`]: struct.BitFlags.html
82    const FLAG_NAMES: &[&[&str]];
83
84    /// Names for combinations of flags, consulted when parsing.
85    ///
86    /// Each entry pairs a bit pattern with the single name that should parse
87    /// to it. When parsing, the input string is matched against all entries;
88    /// the first match wins.
89    ///
90    /// The bit pattern may include bits that are not named in
91    /// [`FLAG_NAMES`], or that are not declared as individual flags at all.
92    /// This const does not validate against
93    /// [`VALID_BITS_MASK`](Self::VALID_BITS_MASK).
94    /// Parsing preserves whatever bits the entry contains.
95    ///
96    /// This const is **not** consulted by [`Display`](core::fmt::Display).
97    /// Formatting a value with multiple bits set always writes the individual
98    /// flag names from [`FLAG_NAMES`], never a combined name.
99    ///
100    /// [`FLAG_NAMES`]: trait.BitFlagsSpec.html#associatedconstant.FLAG_NAMES
101    const COMBINED_NAMES: &[(T, &str)];
102
103    /// Controls whether the default [`FromStr`](core::str::FromStr) and
104    /// `Deserialize` implementations for [`BitFlags`] parse named flags
105    /// case-sensitively.
106    ///
107    /// If [`NameMatcher::Exact`], named flags are matched exactly.
108    /// If [`NameMatcher::AsciiCaseInsensitive`], named flags are matched
109    /// using ASCII case-insensitive comparison.
110    ///
111    /// This value does not affect methods that accept an explicit
112    /// [`NameMatcher`], such as [`BitFlags::try_from_str`].
113    ///
114    /// [`BitFlags`]: struct.BitFlags.html
115    /// [`BitFlags::try_from_str`]: struct.BitFlags.html#method.try_from_str
116    const DEFAULT_NAME_MATCHER: NameMatcher;
117}
118
119/// Controls how flag names are matched when parsing.
120///
121/// # Why `#[non_exhaustive]`
122///
123/// `NameMatcher` is only used as an input parameter to parsing APIs, never
124/// returned as a result.
125/// `#[non_exhaustive]` is appropriate here because new matcher variants can
126/// then be added in minor releases without breaking callers.
127#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
128#[non_exhaustive]
129pub enum NameMatcher {
130    /// Matches flag names exactly.
131    Exact,
132
133    /// Matches flag names using ASCII case-insensitive comparison.
134    AsciiCaseInsensitive,
135}
136
137impl NameMatcher {
138    /// Returns `true` if `given` matches `expected` according to this matcher.
139    #[inline]
140    #[must_use]
141    pub fn matches(self, given: &str, expected: &str) -> bool {
142        match self {
143            Self::Exact => given == expected,
144            Self::AsciiCaseInsensitive => given.eq_ignore_ascii_case(expected),
145        }
146    }
147}