14 stable releases

2.2.3 Nov 4, 2023
2.2.1 Jul 9, 2023
2.1.0 Oct 3, 2022
2.0.0 May 4, 2022
1.1.0 Mar 7, 2021

#325 in No standard library

Download history 11424/week @ 2023-11-21 11437/week @ 2023-11-28 13752/week @ 2023-12-05 13629/week @ 2023-12-12 13376/week @ 2023-12-19 9098/week @ 2023-12-26 11158/week @ 2024-01-02 12309/week @ 2024-01-09 12850/week @ 2024-01-16 13998/week @ 2024-01-23 16087/week @ 2024-01-30 16035/week @ 2024-02-06 13047/week @ 2024-02-13 12962/week @ 2024-02-20 15869/week @ 2024-02-27 12891/week @ 2024-03-05

57,013 downloads per month
Used in 38 crates (16 directly)

MIT/Apache

20KB
285 lines

Bitmask-Enum

API Crate

A bitmask enum attribute macro, to turn an enum into a bitmask.

A bitmask can have (un)signed integer types, the default type is usize.

First created because I wanted something simple, evolved with inspiration from the bitflags crate, which might be something you want to take a look at.

use bitmask_enum::bitmask;

#[bitmask] // usize
enum Bitmask { /* ... */ }

#[bitmask(u8)] // u8
enum BitmaskU8 { /* ... */ }

Example

use bitmask_enum::bitmask;

#[bitmask(u8)]
enum Bitmask {
    Flag1, // defaults to 0b00000001
    Flag2, // defaults to 0b00000010
    Flag3, // defaults to 0b00000100
}

// It is possible to impl on the bitmask and use its bits field
impl Bitmask {
    fn _set_to(&mut self, val: u8) {
        self.bits = val
    }
}

// bitmask has const bitwise operator methods
const CONST_BM: Bitmask = Bitmask::Flag2.or(Bitmask::Flag3);

fn main() {
    println!("{:#010b}", CONST_BM); // 0b00000110

    // Bitmask that contains Flag1 and Flag3
    let bm = Bitmask::Flag1 | Bitmask::Flag3;

    println!("{:#010b}", bm); // 0b00000101

    // Does bm intersect one of CONST_BM
    println!("{}", bm.intersects(CONST_BM)); // true

    // Does bm contain all of CONST_BM
    println!("{}", bm.contains(CONST_BM)); // false
}

Custom Values

You can assign any flag a custom value.

use bitmask_enum::bitmask;

#[bitmask(u8)]
enum Bitmask {
    Flag1, // defaults to 0b00000001

    CustomFlag3 = 0b00000100,

    Flag2, // defaults to 0b00000010
    Flag3, // defaults to 0b00000100

    Flag13_1 = 0b00000001 | 0b00000100,
    Flag13_2 = Self::Flag1.or(Self::Flag3).bits,
    Flag13_3 = Self::Flag1.bits | Self::CustomFlag3.bits,

    Flag123 = {
        let flag13 = Self::Flag13_1.bits;
        flag13 | Self::Flag2.bits
    },
}

fn main() {
    let bm = Bitmask::Flag1 | Bitmask::Flag3;

    println!("{:#010b}", bm); // 0b00000101
    println!("{}", bm == Bitmask::Flag13_1); // true

    println!("{:#010b}", Bitmask::Flag123); // 0b00000111
}

Bitmask Config

It is possible to add custom bitmask config options via the #[bitmask_config(...)] macro. (Just add it below the #[bitmask] macro)

use bitmask_enum::bitmask;

#[bitmask(u8)]
#[bitmask_config(inverted_flags)]
enum Bitmask {
    Flag1, // defaults to 0b00000001
}

#[bitmask(u8)]
#[bitmask_config(vec_debug)]
enum BitmaskVecDebug {
    Flag1,
    Flag2,
}

fn main() {
    println!("{:#010b}", Bitmask::Flag1); // 0b00000001
    println!("{:#010b}", Bitmask::InvertedFlag1); // 0b11111110

    println!("{:?}", BitmaskVecDebug::none()); // BitmaskVecDebug[]
    println!("{:?}", BitmaskVecDebug::Flag1); // BitmaskVecDebug[Flag1]
    println!("{:?}", BitmaskVecDebug::full()); // BitmaskVecDebug[Flag1, Flag2]
}

Available Config Options

  • inverted_flags => Adds an inverted flag for every non-inverted flag to the bitmask.
  • vec_debug => Replaces the default Debug trait implementation with a custom one that prints the bitmask as a vec of all matching values.

If you need / can think of any other config option, feel free to suggest them and we can discuss implementing them.

Implemented Methods

// Returns the underlying bits of the bitmask.
const fn bits(&self) -> #type;

// Returns a bitmask that contains all values.
//
// This will include bits that do not have any flags.
// Use `::full()` if you only want to use flags.
const fn all() -> Self;

// Returns `true` if the bitmask contains all values.
//
// This will check for `bits == !0`,
// use `.is_full()` if you only want to check for all flags
const fn is_all(&self) -> bool;

// Returns a bitmask that does not contain any values.
const fn none() -> Self;

// Returns `true` if the bitmask does not contain any values.
const fn is_none(&self) -> bool;

// Returns a bitmask that contains all flags.
const fn full() -> Self;

// Returns `true` if the bitmask contains all flags.
//
// This will fail if any unused bit is set,
// consider using `.truncate()` first.
const fn is_full(&self) -> bool;

// Returns a bitmask that only has bits corresponding to flags
const fn truncate(&self) -> Self;

// Returns `true` if `self` intersects with any value in `other`,
// or if `other` does not contain any values.
// This is equivalent to `(self & other) != 0 || other == 0`.
const fn intersects(&self, other: Self) -> bool;

// Returns `true` if `self` contains all values of `other`.
// This is equivalent to  `(self & other) == other`.
const fn contains(&self, other: Self) -> bool;

// Constant bitwise operations.
const fn not(self) -> Self;
const fn and(self, other: Self) -> Self;
const fn or(self, other: Self) -> Self;
const fn xor(self, other: Self) -> Self;

Implemented Traits

#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]

impl core::ops::Not;

impl core::ops::BitAnd;
impl core::ops::BitAndAssign;

impl core::ops::BitOr;
impl core::ops::BitOrAssign;

impl core::ops::BitXor;
impl core::ops::BitXorAssign;

impl From<#type> for #ident;
impl From<#ident> for #type;

impl PartialEq<#type>;

impl core::fmt::Binary;
impl core::fmt::LowerHex;
impl core::fmt::UpperHex;
impl core::fmt::Octal;

Dependencies

~285–730KB
~17K SLoC