bitflags-lite 0.2.0

Lightweight generics-based bitflags
Documentation
//! The `bitflags-lite` crate provides a lightweight, generic bitflags
//! implementation for Rust.
//!
//! [`BitFlags<T, U>`] is a `#[repr(transparent)]` wrapper around an integer `T`
//! that adds a compile-time tag type `U`.
//! The tag distinguishes flag sets that share the same storage type and
//! describes which bits are valid through the [`BitFlagsSpec`] trait.
//!
//! Flag sets can be defined either through the [`bitflags!`] macro, which
//! generates the trait implementation and named constants from a compact
//! declaration, or by implementing [`BitFlagsSpec`] directly.
//! Both forms produce the same types and the same runtime behavior.
//!
//! It is `no_std` and `no_alloc` compatible, and const-friendly.
//!
//!
//! # Why a generic `BitFlags<T, U>`?
//!
//! Macro-based bitflags crates generate a distinct struct for every flag set.
//! Each invocation re-expands the full method set, and the resulting types
//! share no common trait or type with one another.
//! By contrast, `bitflags-lite` defines [`BitFlags<T, U>`] once and asks you to
//! specialize it through a plain trait implementation.
//! The provided [`bitflags!`] macro is a convenience wrapper that generates
//! that trait impl and the named constants — it does not generate a new type.
//!
//! That design has a few concrete consequences:
//!
//! - **Generic code over flag sets.** Because every flag set is the same type
//!   family, you can write `fn f<T, U: BitFlagsSpec<T>>(flags: BitFlags<T,
//!   U>)`. This is not possible with macro-generated structs.
//! - **Predictable expansion.** A new flag set adds a small trait impl and a
//!   handful of constants, not a fresh copy of the entire API.
//! - **Inspectable source.** Even when using [`bitflags!`], the spec is
//!   ordinary Rust that `rustdoc` renders, `rust-analyzer` navigates, and
//!   `grep` finds. The macro expansion is small and readable via `cargo
//!   expand`.
//!
//!
//! # Two ways to define flags
//!
//! The same flag set can be defined manually or through the macro.
//! The manual form is fully explicit.
//! The macro form is more concise and infers flag names, valid bits, and
//! combined-name tables from the declarations.
//!
//! Both produce identical types and APIs, and both can be mixed in one crate —
//! some flag sets defined by hand, others via the macro — because the manual
//! form is the primitive and the macro only builds on it.
//!
//! Choose the manual form when you want complete control over the trait impl
//! or want to avoid macros in your codebase.
//! Choose the macro form when you want the convenience and don't mind the
//! extra layer.
//!
//! See [Examples](#examples) below for both forms side by side.
//!
//!
//! # When to choose `bitflags`, `enumflags2`, or `bitflags-lite`
//!
//! - Choose [`bitflags`] if you want the mature and battle-tested crate, and
//!   the ecosystem-standard macro.
//! - Choose [`enumflags2`] if you want a single-flag type distinct from the set
//!   type, enum-variant-driven definitions, and match/iter over variants.
//! - Choose [`bitflags-lite`] if you want a reusable generic [`BitFlags<T, U>`]
//!   with a small, inspectable expansion, optionally generated by a concise
//!   macro. It offers a per-bit naming model, and a clear single-flag
//!   ([`BitFlag<T, U>`]) vs. set ([`BitFlags<T, U>`]) distinction.
//!
//!
//! # Usage
//!
//! ```toml
//! [dependencies]
//! bitflags-lite = "0.2.0"
//! ```
//!
//! With optional features:
//!
//! ```toml
//! [dependencies]
//! bitflags-lite = { version = "0.2.0", features = [ "serde", "bytemuck" ] }
//! ```
//!
//!
//! # Examples
//!
//! ## With the [`bitflags!`] macro
//!
//! The macro generates the tag type, the flag-set alias, the [`BitFlagsSpec`]
//! implementation, and the named constants from one compact declaration:
//!
//! ```rust
//! use core::str::FromStr;
//!
//! use bitflags_lite::{BitFlag, BitFlags, BitFlagsSpec, NameMatcher};
//!
//! bitflags_lite::bitflags! {
//!     #![storage(u32)]
//!
//!     pub struct PermissionsTag;
//!
//!     pub bit READ  = 0, &["read"];
//!     pub bit WRITE = 1, &["write"];
//!     pub bit EXEC  = 2, &["exec", "execute"];
//!
//!     pub mask RWX  = [READ, WRITE, EXEC], "rwx";
//!     pub mask NONE = 0, "none";
//! }
//!
//! pub type Permission = BitFlag<u32, PermissionsTag>;
//! pub type Permissions = BitFlags<u32, PermissionsTag>;
//!
//! let rwx = READ | WRITE | EXEC;
//! assert!(rwx.contains(READ.into()));
//! assert!(rwx.contains(WRITE.into()));
//! assert!(rwx.contains(EXEC.into()));
//! assert!(rwx.contains(RWX));
//!
//! let read_exec = READ | EXEC;
//! assert!(!read_exec.contains(WRITE.into()));
//!
//! // Combined names participate in parsing.
//! let parsed = Permissions::from_str("rwx").unwrap();
//! assert_eq!(format!("{parsed}"), "read | write | exec");
//!
//! let parsed = Permissions::from_str("read | 0x18 | execute").unwrap();
//! assert_eq!(format!("{parsed}"), "read | exec | 0x18");
//!
//! // Iteration yields individual BitFlag values.
//! let flags: Vec<Permission> = parsed.into_iter().collect();
//! assert_eq!(
//!     flags,
//!     [
//!         READ,
//!         EXEC,
//!         BitFlag::from_bit_retain(3),
//!         BitFlag::from_bit_retain(4),
//!     ]
//! );
//!
//! let formatted: Vec<String> = flags.iter().map(ToString::to_string).collect();
//! assert_eq!(formatted, ["read", "exec", "0x8", "0x10"]);
//!
//! let named_only: Vec<&'static str> = flags.iter().filter_map(BitFlag::name).collect();
//! assert_eq!(named_only, ["read", "exec"]);
//! ```
//!
//! ## Without the macro
//!
//! The same flag set can be written manually.
//! This is what the macro expands to, and it is fully supported as a public
//! API:
//!
//! ```rust
//! use core::str::FromStr;
//!
//! use bitflags_lite::{BitFlag, BitFlags, BitFlagsSpec, NameMatcher};
//!
//! #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default, Debug)]
//! struct PermissionsTag;
//!
//! impl BitFlagsSpec<u32> for PermissionsTag {
//!     const VALID_BITS_MASK: u32 = 0b111;
//!     const FLAG_NAMES: &[&[&str]] = &[&["read"], &["write"], &["exec", "execute"]];
//!     const COMBINED_NAMES: &[(u32, &str)] = &[(0, "none"), (0b111, "rwx")];
//!     const DEFAULT_NAME_MATCHER: NameMatcher = NameMatcher::Exact;
//! }
//!
//! type Permission = BitFlag<u32, PermissionsTag>;
//! type Permissions = BitFlags<u32, PermissionsTag>;
//!
//! // Constructors are `const` for concrete integer types.
//! const READ: Permission = Permission::try_from_bit(0).unwrap();
//! const WRITE: Permission = Permission::try_from_bit(1).unwrap();
//! const EXEC: Permission = Permission::try_from_bit(2).unwrap();
//! const RWX: Permissions = Permissions::try_from_bits(0b111).unwrap();
//! ```
//!
//! Constants can also be defined through an inherent implementation, a trait,
//! or an associated `const` on the tag type.
//!
//! ## Type-safety across flag sets
//!
//! The tag type keeps flag sets distinct even when they share a storage type.
//! The following example deliberately fails to compile:
//!
//! ```rust,compile_fail
//! use bitflags_lite::{BitFlags, BitFlagsSpec, NameMatcher};
//!
//! struct PermsTag;
//! type Perms = BitFlags<u8, PermsTag>;
//! impl BitFlagsSpec<u8> for PermsTag {
//!     const VALID_BITS_MASK: u8 = (1 << 3) - 1;
//!     const FLAG_NAMES: &[&[&str]] = &[&["read"], &["write"], &["exec", "execute"]];
//!     const COMBINED_NAMES: &[(u8, &str)] = &[(0, "none"), (0b111, "rwx")];
//!     const DEFAULT_NAME_MATCHER: NameMatcher = NameMatcher::AsciiCaseInsensitive;
//! }
//!
//! struct KeyboardModifiersTag;
//! type KeyboardModifiers = BitFlags<u8, KeyboardModifiersTag>;
//! impl BitFlagsSpec<u8> for KeyboardModifiersTag {
//!     const VALID_BITS_MASK: u8 = (1 << 4) - 1;
//!     const FLAG_NAMES: &[&[&str]] = &[&["Shift"], &["Ctrl"], &["Alt"], &["Meta"]];
//!     const COMBINED_NAMES: &[(u8, &str)] = &[];
//!     const DEFAULT_NAME_MATCHER: NameMatcher = NameMatcher::AsciiCaseInsensitive;
//! }
//!
//! let rwx: Perms = Perms::try_from_bits(0b111).unwrap();
//! let keyboard_modifiers: KeyboardModifiers = rwx;
//! //                                          ^^^
//! // ❌ error[E0308]: mismatched types
//! // expected struct `BitFlags<_, KeyboardModifiersTag>`
//! //    found struct `BitFlags<_, test::PermsTag>`
//! ```
//!
//!
//! # Design Highlights
//!
//! - **Single-flag vs. set distinction.** [`BitFlag<T, U>`] represents one bit
//!   index. [`BitFlags<T, U>`] represents a set. They convert into each other
//!   and interoperate through the usual operators.
//! - **Invalid bits are representable.** A [`BitFlags`] value may contain bits
//!   not set in [`BitFlagsSpec::VALID_BITS_MASK`]; they are preserved by
//!   raw-bit operations and cleared by [`BitFlags::truncate`],
//!   [`BitFlags::complement`], and [`Not`](core::ops::Not). Use
//!   [`BitFlags::is_valid`] to check.
//! - **Const-friendly.** Every constructor, accessor, and combinator on the
//!   concrete integer types is a `const fn`. The generic equivalents live in
//!   the [`BitFlagsExt`] and [`BitFlagExt`] extension traits.
//! - **No allocation.** Parsing and formatting work without `alloc`. The
//!   `alloc` feature only enriches error messages with the offending input.
//! - **No hidden type generation.** Even with the [`bitflags!`] macro, no new
//!   type is created per flag set. The macro builds on the same generic
//!   [`BitFlags<T, U>`], so all flag sets remain one type family.
//!
//!
//! # Comparison with Existing Crates
//!
//! | Aspect | `bitflags-lite` | `bitflags` v2 | `enumflags2` v0.7 |
//! |:--:|:--:|:--:|:--:|
//! | Definition mechanism | [`bitflags!`] macro or a manual `BitFlagsSpec<T>` impl | `bitflags! { ... }` macro | `#[bitflags]` attribute macro |
//! | Generated/used type | One generic `BitFlags<T, U>` over a tag type `U` | A distinct user-owned struct per flag set | `BitFlags<T, N = T::Numeric>` over an enum |
//!
//! | Feature | `bitflags-lite` | `bitflags` v2 | `enumflags2` v0.7 |
//! |:--:|:--:|:--:|:--:|
//! | Generic code over flag sets possibility | ✅ Yes | ⛔️ No | ⛔️ No |
//! | Macro-generated bit flags | ✅ Yes | ✅ Yes | ✅ Yes |
//! | Most methods const | ✅ Yes | ✅ Yes | ⚠️ Limited |
//! | Multi-bit flags | ⚠️ Partial | ✅ Yes | ⛔️ No |
//! | Single-flag vs set distinction | ✅ Yes | ⛔️ No | ✅ Yes |
//! | Alias names for parsing | ✅ Yes | ⛔️ No | ⛔️ No |
//! | Rename a flag | ✅ Yes | ✅ Yes | ⛔️ No |
//! | Custom default | ⛔️ No | ✅ Yes | ✅ Yes |
//! | Only valid bits | ⛔️ No | ⛔️ No | ✅ Yes |
//! | Are invalid bits allowed? | ✅ Yes | ✅ Yes | ⛔️ No |
//! | Format with names | ✅ Yes | ✅ Yes | ✅ Yes |
//! | Parse from names | ✅ Yes | ✅ Yes | ⛔️ No |
//! | Case-insensitive parsing | ✅ Yes | ⛔️ No | ⛔️ No |
//!
//! <!---
//!   Invalid bits are really not allowed in `enumflags2`.
//!   Its `from_bits_unchecked` unsafe method requires that
//!   "All bits set in val must correspond to a value of the enum".
//! -->
//!
//!
//! # Documentation
//!
//! [API Documentation]
//!
//!
//! # Feature flags
//!
//!
//! - `alloc` (implied by `std`, enabled by default): enriches parse errors with
//!   the offending input.
//! - `std` (enabled by default): enables the `alloc` feature.
//! - `arbitrary`: implements `Arbitrary` for `BitFlag<T, U>` and `BitFlags<T,
//!   U>`.
//! - `serde`: implements `serde::Serialize` and `serde::Deserialize` for
//!   `BitFlag<T, U>` and `BitFlags<T, U>`, round-tripping through flag strings.
//! - `bytemuck`: implements `Zeroable` and `Pod` for `BitFlag<T, U>` and
//!   `BitFlags<T, U>`, allowing them to be used in plain-old-data contexts.
//!
//!
//! # License
//!
//! Licensed under either of
//!
//! - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <https://www.apache.org/licenses/LICENSE-2.0>)
//! - MIT license ([LICENSE-MIT](LICENSE-MIT) or <https://opensource.org/licenses/MIT>)
//!
//! at your option.
//!
//!
//! # Contribution
//!
//! Unless you explicitly state otherwise, any contribution intentionally
//! submitted for inclusion in the work by you, as defined in the Apache-2.0
//! license, shall be dual licensed as above, without any
//! additional terms or conditions.
//!
//! [API Documentation]: https://docs.rs/bitflags-lite/latest/bitflags-lite/
//! [`bitflags`]: https://crates.io/crates/bitflags
//! [`enumflags2`]: https://crates.io/crates/enumflags2
//! [`bitflags-lite`]: https://crates.io/crates/bitflags-lite
//! [`bitflags!`]: macro.bitflags.html
//! [`BitFlag<T, U>`]: struct.BitFlag.html
//! [`BitFlags<T, U>`]: struct.BitFlags.html
//! [`BitFlags`]: struct.BitFlags.html
//! [`BitFlagExt`]: trait.BitFlagExt.html
//! [`BitFlagsExt`]: trait.BitFlagsExt.html
//! [`BitFlagsSpec`]: trait.BitFlagsSpec.html

#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]

#[cfg(feature = "alloc")]
extern crate alloc;

#[cfg(feature = "std")]
extern crate std;

mod flag;
mod flag_ext;
mod flags;
mod flags_ext;
mod iter;
mod macros;
mod spec;
pub mod util;

pub use flag::*;
pub use flag_ext::*;
pub use flags::*;
pub use flags_ext::*;
pub use iter::*;
pub use spec::*;

#[cfg(all(test, not(doc)))]
mod integration_tests_deps {
    use serde_test as _;
    use version_sync as _;
}