Skip to main content

bitflags_lite/
lib.rs

1//! The `bitflags-lite` crate provides a lightweight, macro-free, generic
2//! bitflags implementation for Rust.
3//!
4//! [`BitFlags<T, U>`] is a `#[repr(transparent)]` wrapper around an integer `T`
5//! that adds a compile-time tag type `U`.
6//! The tag distinguishes flag sets that share the same storage type and
7//! describes which bits are valid through the [`BitFlagsSpec`] trait.
8//!
9//! It is `no_std` and `no_alloc` compatible, and const-friendly.
10//!
11//!
12//! # Why a generic `BitFlags<T, U>`?
13//!
14//! Macro-based bitflags crates generate a distinct struct for every flag set.
15//! Each invocation re-expands the full method set, and the resulting types
16//! share no common trait or type with one another.
17//! By contrast, `bitflags-lite` defines [`BitFlags<T, U>`] once and asks you to
18//! specialize it through a plain trait implementation.
19//!
20//! That design has a few concrete consequences:
21//!
22//! - **Generic code over flag sets.** Because every flag set is the same type
23//!   family, you can write `fn f<T, U: BitFlagsSpec<T>>(flags: BitFlags<T,
24//!   U>)`. This is not possible with macro-generated structs.
25//! - **Predictable expansion.** A new flag set adds a small trait impl, not a
26//!   fresh copy of the entire API.
27//! - **Inspectable source.** The spec is ordinary Rust. `rustdoc` renders it,
28//!   `rust-analyzer` navigates it, `grep` finds it.
29//!
30//! But you have to give up automatic derivation of flag names and valid bits
31//! and write [`BitFlagsSpec::VALID_BITS_MASK`] and [`BitFlagsSpec::FLAG_NAMES`]
32//! by hand and keep them in sync with your constants.
33//!
34//!
35//! # When to choose `bitflags`, `enumflags2`, or `bitflags-lite`
36//!
37//! - Choose [`bitflags`] if you want the mature and battle-tested crate, the
38//!   ecosystem-standard macro, multi-bit convenience flags, and unnamed
39//!   wildcard flags.
40//! - Choose [`enumflags2`] if you want a single-flag type distinct from the set
41//!   type, enum-variant-driven definitions, and match/iter over variants.
42//! - Choose [`bitflags-lite`] if you want a lightweight flags definition with
43//!   no macros and a reusable generic [`BitFlags<T, U>`], are comfortable with
44//!   a per-bit naming model, and you don't need combined or multi-bit flags.
45//!
46//!
47//! # Usage
48//!
49//! ```toml
50//! [dependencies]
51//! bitflags-lite = "0.1.0"
52//! ```
53//!
54//! With optional features:
55//!
56//! ```toml
57//! [dependencies]
58//! bitflags-lite = { version = "0.1.0", features = [ "serde", "bytemuck" ] }
59//! ```
60//!
61//!
62//! # Examples
63//!
64//! Define a tag type and implement [`BitFlagsSpec`] for it:
65//!
66//! ```rust
67//! use core::str::FromStr;
68//!
69//! use bitflags_lite::{BitFlag, BitFlags, BitFlagsSpec, NameMatcher};
70//!
71//! #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default, Debug)]
72//! struct PermissionsTag;
73//!
74//! impl BitFlagsSpec<u32> for PermissionsTag {
75//!     const VALID_BITS_MASK: u32 = (1 << 3) - 1;
76//!     const FLAG_NAMES: &[&[&str]] = &[&["read"], &["write"], &["exec", "execute"]];
77//!     const DEFAULT_NAME_MATCHER: NameMatcher = NameMatcher::Exact;
78//! }
79//!
80//! type Permission = BitFlag<u32, PermissionsTag>;
81//! type Permissions = BitFlags<u32, PermissionsTag>;
82//!
83//! // Constructors are `const` for concrete integer types.
84//! const READ: Permission = Permission::try_from_bit(0).unwrap();
85//! const WRITE: Permission = Permission::try_from_bit(1).unwrap();
86//! const EXEC: Permission = Permission::try_from_bit(2).unwrap();
87//!
88//! let rwx = READ | WRITE | EXEC;
89//! assert!(rwx.contains(READ.into()));
90//! assert!(rwx.contains(WRITE.into()));
91//! assert!(rwx.contains(EXEC.into()));
92//! assert!(rwx.contains(READ | WRITE));
93//!
94//! let read_exec = READ | EXEC;
95//! assert!(!read_exec.contains(WRITE.into()));
96//!
97//! let flags = Permissions::from_str("read | 0x18 | write").unwrap();
98//! assert_eq!(format!("{flags}"), "read | write | 0x18");
99//!
100//! let flags: Vec<Permission> = flags.into_iter().collect();
101//! assert_eq!(
102//!     flags,
103//!     [
104//!         READ,
105//!         WRITE,
106//!         Permission::from_bit_retain(3),
107//!         Permission::from_bit_retain(4),
108//!     ]
109//! );
110//!
111//! let formatted: Vec<String> = flags.iter().map(ToString::to_string).collect();
112//! assert_eq!(formatted, ["read", "write", "0x8", "0x10"]);
113//!
114//! let named_only: Vec<&'static str> = flags.iter().filter_map(BitFlag::name).collect();
115//! assert_eq!(named_only, ["read", "write"]);
116//! ```
117//!
118//! Constants can also be defined through an inherent implementation, a trait,
119//! or an associated `const` on the tag type — whatever fits your code style.
120//!
121//! The tag type keeps flag sets distinct even when they share a storage type,
122//! providing type-safety across otherwise-similar types:
123//!
124//! The following example deliberately fails to compile:
125//! ```rust,compile_fail
126//! use bitflags_lite::{BitFlags, BitFlagsSpec, NameMatcher};
127//!
128//! struct PermsTag;
129//! type Perms = BitFlags<u8, PermsTag>;
130//! impl BitFlagsSpec<u8> for PermsTag {
131//!     const VALID_BITS_MASK: u8 = (1 << 3) - 1;
132//!     const FLAG_NAMES: &[&[&str]] = &[&["read"], &["write"], &["exec", "execute"]];
133//!     const DEFAULT_NAME_MATCHER: NameMatcher = NameMatcher::AsciiCaseInsensitive;
134//! }
135//!
136//! struct KeyboardModifiersTag;
137//! type KeyboardModifiers = BitFlags<u8, KeyboardModifiersTag>;
138//! impl BitFlagsSpec<u8> for KeyboardModifiersTag {
139//!     const VALID_BITS_MASK: u8 = (1 << 4) - 1;
140//!     const FLAG_NAMES: &[&[&str]] = &[&["Shift"], &["Ctrl"], &["Alt"], &["Meta"]];
141//!     const DEFAULT_NAME_MATCHER: NameMatcher = NameMatcher::AsciiCaseInsensitive;
142//! }
143//!
144//! let rwx: Perms = Perms::try_from_bits(0b111).unwrap();
145//! let keyboard_modifiers: KeyboardModifiers = rwx;
146//! //                                          ^^^
147//! // ❌ error[E0308]: mismatched types
148//! // expected struct `BitFlags<_, KeyboardModifiersTag>`
149//! //    found struct `BitFlags<_, test::PermsTag>`
150//! ```
151//!
152//!
153//! # Design Highlights
154//!
155//! - **Single-flag vs. set distinction.** [`BitFlag<T, U>`] represents one bit
156//!   index. [`BitFlags<T, U>`] represents a set. They convert into each other
157//!   and interoperate through the usual operators.
158//! - **Invalid bits are representable.** A [`BitFlags`] value may contain bits
159//!   not set in [`BitFlagsSpec::VALID_BITS_MASK`]; they are preserved by
160//!   raw-bit operations and cleared by [`BitFlags::truncate`],
161//!   [`BitFlags::complement`], and [`Not`](core::ops::Not). Use
162//!   [`BitFlags::is_valid`] to check.
163//! - **Const-friendly.** Every constructor, accessor, and combinator on the
164//!   concrete integer types is a `const fn`. The generic equivalents live in
165//!   the [`BitFlagsExt`] and [`BitFlagExt`] extension traits.
166//! - **No allocation.** Parsing and formatting work without `alloc`. The
167//!   `alloc` feature only enriches error messages with the offending input.
168//!
169//!
170//! # Comparison with Existing Crates
171//!
172//! | Aspect | `bitflags-lite` | `bitflags` v2 | `enumflags2` v0.7 |
173//! |:--:|:--:|:--:|:--:|
174//! | Definition mechanism | Trait `BitFlagsSpec<T>` for a tag type | `bitflags! { ... }` macro | `#[bitflags]` attribute macro |
175//! | Generated/used type | `BitFlags<T, U>` over a tag type `U` | User-owned struct | `BitFlags<T, N = T::Numeric>` over an enum |
176//!
177//! | Feature | `bitflags-lite` | `bitflags` v2 | `enumflags2` v0.7 |
178//! |:--:|:--:|:--:|:--:|
179//! | Automatically deduced flag names | ⛔️ No | ✅ Yes | ✅ Yes |
180//! | Automatically deduced valid bits | ⛔️ No | ✅ Yes | ✅ Yes |
181//! | Automatically sync names and valid bits | ⛔️ No | ✅ Yes | ✅ Yes |
182//! | Most methods const | ✅ Yes | ✅ Yes | ⚠️ Limited |
183//! | Multi-bit flags | ⛔️ No | ✅ Yes | ⛔️ No |
184//! | Single-flag vs set distinction | ✅ Yes | ⛔️ No | ✅ Yes |
185//! | Alias names for parsing | ✅ Yes | ⛔️ No | ⛔️ No |
186//! | Rename a flag | ✅ Yes | ✅ Yes | ⛔️ No |
187//! | Custom default | ⛔️ No | ✅ Yes | ✅ Yes |
188//! | Only valid bits | ⛔️ No | ⛔️ No | ✅ Yes |
189//! | Are invalid bits allowed? | ✅ Yes | ✅ Yes | ⛔️ No |
190//! | Format with names | ✅ Yes | ✅ Yes | ✅ Yes |
191//! | Parse from names | ✅ Yes | ✅ Yes | ⛔️ No |
192//! | Case-insensitive parsing | ✅ Yes | ⛔️ No | ⛔️ No |
193//!
194//! <!---
195//! - Invalid bits are really not allowed in `enumflags2`. Its
196//!   `from_bits_unchecked` unsafe method requires that "All bits set in val
197//!   must correspond to a value of the enum".
198//! -->
199//!
200//!
201//! # Documentation
202//!
203//! [API Documentation]
204//!
205//!
206//! # Feature flags
207//!
208//!
209//! - `alloc` (implied by `std`, enabled by default): enriches parse errors with
210//!   the offending input.
211//! - `std` (enabled by default): enables `alloc` feature.
212//! - `arbitrary`: implements `Arbitrary` for `BitFlag<T, U>` and `BitFlags<T,
213//!   U>`.
214//! - `serde`: implements `serde::Serialize` and `serde::Deserialize` for
215//!   `BitFlag<T, U>` and `BitFlags<T, U>` round-tripping through flag strings.
216//! - `bytemuck`: implements `Zeroable` and `Pod` for `BitFlag<T, U>` and
217//!   `BitFlags<T, U>`, allowing them to be used in plain-old-data contexts.
218//!
219//!
220//! # License
221//!
222//! Licensed under either of
223//!
224//! - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <https://www.apache.org/licenses/LICENSE-2.0>)
225//! - MIT license ([LICENSE-MIT](LICENSE-MIT) or <https://opensource.org/licenses/MIT>)
226//!
227//! at your option.
228//!
229//!
230//! # Contribution
231//!
232//! Unless you explicitly state otherwise, any contribution intentionally
233//! submitted for inclusion in the work by you, as defined in the Apache-2.0
234//! license, shall be dual licensed as above, without any
235//! additional terms or conditions.
236//!
237//! [API Documentation]: https://docs.rs/bitflags-lite/latest/bitflags-lite/
238//! [`bitflags`]: https://crates.io/crates/bitflags
239//! [`enumflags2`]: https://crates.io/crates/enumflags2
240//! [`bitflags-lite`]: https://crates.io/crates/bitflags-lite
241//! [`BitFlag<T, U>`]: struct.BitFlag.html
242//! [`BitFlags<T, U>`]: struct.BitFlags.html
243//! [`BitFlags`]: struct.BitFlags.html
244//! [`BitFlagExt`]: trait.BitFlagExt.html
245//! [`BitFlagsExt`]: trait.BitFlagsExt.html
246//! [`BitFlagsSpec`]: trait.BitFlagsSpec.html
247
248#![no_std]
249#![cfg_attr(docsrs, feature(doc_cfg))]
250
251#[cfg(feature = "alloc")]
252extern crate alloc;
253
254#[cfg(feature = "std")]
255extern crate std;
256
257mod flag;
258mod flag_ext;
259mod flags;
260mod flags_ext;
261mod iter;
262mod spec;
263pub mod util;
264
265pub use flag::*;
266pub use flag_ext::*;
267pub use flags::*;
268pub use flags_ext::*;
269pub use iter::*;
270pub use spec::*;
271
272#[cfg(all(test, not(doc)))]
273mod integration_tests_deps {
274    use serde_test as _;
275    use version_sync as _;
276}