21 releases (11 breaking)

new 0.12.0 Feb 21, 2025
0.10.1 Feb 9, 2025
0.8.2 Dec 29, 2024
0.7.2 Nov 25, 2024
0.3.0 May 16, 2024

#27 in No standard library

Download history 4/week @ 2024-10-30 6/week @ 2024-11-06 418/week @ 2024-11-13 424/week @ 2024-11-20 94/week @ 2024-11-27 53/week @ 2024-12-04 26/week @ 2024-12-11 371/week @ 2024-12-18 271/week @ 2024-12-25 16/week @ 2025-01-01 14/week @ 2025-01-08 133/week @ 2025-01-15 124/week @ 2025-01-22 6/week @ 2025-01-29 150/week @ 2025-02-05 309/week @ 2025-02-12

598 downloads per month

MIT/Apache

76KB
1.5K SLoC

bitflag-attr

Rust Latest version Documentation License

This is a proc-macro Rust crate that allows to turn a C-like enum into a bitflag types with an ergonomic end-user API.

You can use this crate to:

  • Provide more user-friendly bindings to C APIs where flags may or may not be fully known in advance.
  • Generate efficient options types with string parsing and formatting support.

You can't use this crate to:

  • Guarantee only bits corresponding to defined flags will ever be set. bitflag-attr allows access to the underlying bits type so arbitrary bits may be set.

  • Define bitfields. bitflag-attr only generates types where set bits denote the presence of some combination of flags.

  • Documentation

Usage

Add this to your Cargo.toml:

[dependencies]
bitflag-attr = "0.11.1"

and this to your source code:

use bitflag_attr::bitflag;

Quick Example

Generate a flags structure:

use bitflag_attr::bitflag;

#[bitflag(u32)]
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
enum Flags {
    /// The value `A`, at bit position `0`.
    A = 0b00000001,
    /// The value `B`, at bit position `1`.
    B = 0b00000010,
    /// The value `C`, at bit position `2`.
    C = 0b00000100,

    /// The combination of `A`, `B`, and `C`.
    ABC = A | B | C,
}

fn main() {
    let e1 = Flags::A | Flags::C;
    let e2 = Flags::B | Flags::C;
    assert_eq!((e1 | e2), Flags::ABC);   // union
    assert_eq!((e1 & e2), Flags::C);     // intersection
    assert_eq!((e1 - e2), Flags::A);     // set difference
    assert_eq!(!e2, Flags::A);           // set complement
}

If you don't want Debug trait to be generated, you can simply not define it on the derive attribute.

use bitflag_attr::bitflag;

#[bitflag(u32)]
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
enum Flags {
    /// The value `A`, at bit position `0`.
    A = 0b00000001,
    /// The value `B`, at bit position `1`.
    B = 0b00000010,
    /// The value `C`, at bit position `2`.
    C = 0b00000100,

    /// The combination of `A`, `B`, and `C`.
    ABC = A | B | C,
}

Features

  • Use enum native syntax to define individual known flags
  • Discriminant values must be defined
  • Generated end-user API almost entirely the same to bitflags crate
  • Most of the generated type-associated API is const-compatible (entirely if const-mut-ref feature flag enabled and Rust version is 1.83.0 or superior)
  • Debug formatter outputs both the binary, octal, hexadecimal and human-readable representations
  • Optional support for serialization with the serde feature flag
  • Optional support for bytemuck's casting between flags values and their underlying bits values with the bytemuck feature flag
  • Optional support for generating structured data with the arbitrary feature flag
  • Compatible with #[no_std]

Implemented traits

The macro requires that Clone and Copy are derived.

The macro will also implement some traits for bitwise operations and formatting.

  • core::ops::Not
  • core::ops::BitAnd
  • core::ops::BitOr
  • core::ops::BitXor
  • core::ops::BitAndAssign
  • core::ops::BitOrAssign
  • core::ops::BitXorAssign
  • core::ops::Sub
  • core::ops::SubAssign
  • core::fmt::Debug (if on the derive macro list)
  • core::default::Default (if on the derive macro list)
  • core::fmt::Binary
  • core::fmt::UpperHex
  • core::fmt::LowerHex
  • core::fmt::Octal
  • core::str::FromStr
  • core::iter::Extend
  • core::iter::FromIterator
  • core::iter::IntoIterator (for the type and reference)
  • From

If the Debug trait is defined in the #[derive(...)] attribute. The macro will produce a custom implementation instead of the one Rust std produces

There is a opt-in crate feature serde that generate a parsing error type and implements the traits:

  • serde::Serialize
  • serde::Deserialize

The custom implementation for Serialize and Deserialize will be generated only if those traits are in the #[derive(...)] attribute list (similar how the Debug works).

Note: This crate does not import/re-export serde traits, your project MUST have serde as dependency.

There is a opt-in crate feature arbitrary that generate a parsing error type and implements the traits:

  • arbitrary::Arbitrary

The custom implementation for Arbitrary will be generated only if this traits are in the #[derive(...)] attribute list (similar how the Debug works).

Note: This crate does not import/re-export arbitrary traits, your project MUST have arbitrary as dependency.

There is a opt-in crate feature bytemuck that generate a parsing error type and implements the traits:

  • bytemuck::Pod
  • bytemuck::Zeroable

The custom implementation for Pod and Zeroable will be generated only if those traits are in the #[derive(...)] attribute list (similar how the Debug works).

Const mut ref

Most of the associated function generated for the flags type are const-compatible, with exceptions with the one that takes &mut self.

If you are on Rust version 1.83.0 or superior, you can enable the const-mut-ref feature flag to make those function to also be const-compatible.

Alternatives

Rust Version Support

The minimum supported Rust version is documented in the Cargo.toml file. This may be bumped in minor releases as necessary.

Dependencies

~210–650KB
~15K SLoC