Crate bitmask [] [src]

A bitmask generator for enum scoped bit flags.

The bitmask! macro creates a struct and an enum that holds your flags. The enum contains all the bit flag variants and the struct is a mixture of those bit flags called a bitmask. It's syntax is as follows:

bitmask! {
    pub mask <struct_name>: <struct_type> where flags <enum_name> {
        <flag_name> = <value>,
        ...
    }
}

where pub is optional and struct_type can be one of the primitive integer types (i8-64, u8-64, isize, usize).

Application

Sometimes you might want to wrap some lib that ports C or some other code through FFI which exposes numerous defines/constants as const. Lets take a look at this example module:

mod tex {
    ...
    pub const TEXTURE_2D: u32   = 1;
    pub const TEXTURE_3D: u32   = 2;
    pub const FLIP: u32         = 4;
    ...
    pub fn set_options(mask: u32) { ... }
}

To avoid collisions you would use these through the mod scope like so:

tex::set_options(tex::TEXTURE_2D | tex::FLIP);

But that does not guarantee you that you won't use invalid flag values. For example you could do:

set_options(3 | 8);

Now imagine you had an enum to hold all of those flags and a common type that does not accept any types other than the enum variants and itself. This is exactly what bitmask! does for you! It generates an enum with the variants (flags) you supply and a struct that holds a mask which is a mixture of these variants. So now our example would look like this:

bitmask! {
    pub mask TexMask: u32 where flags TexOption {
        Texture2d = tex::TEXTURE_2D,
        Texture3d = tex::TEXTURE_3D,
        Flip = tex::FLIP
    }
}

fn set_options(mask: TexMask) {
    tex::set_options(*mask);
}

// Single flag
set_options(TexOption::Texture2d.into());
set_options(TexMask::from(TexOption::Texture3d));

// Multiple flags
set_options(TexOption::Texture2d | TexOption::Flip);

Things that are doable but can change with time:

If for some reason you want to define the enum and the struct yourself you can do so and use the @IMPL branch of the macro to implement the methods. The only restrictions are that your struct's inner field must be named mask and the enum should have the same size as the struct which can be achieved through the #[repr()] modifier with the same integer type as the field mask.

Implementing Into<struct_name> and Deref for your own custom type is possible if you want to use it with the preimplemented methods for the mask but does not apply for the trait implements like BitOr for example.

Examples:

bitmask! {
    mask BitMask: u32 where flags Flags {
        Flag1       = 0x00000001,
        Flag2       = 0x000000F0,
        Flag3       = 0x00000800,
        Flag123     = 0x000008F1,
        // Note that function calls like `isize::min_value()`
        // can't be used for enum discriminants in Rust.
        FlagMax     = ::std::u32::MAX
    }
}

let mut mask = BitMask::none();

mask.set(Flags::Flag1 | Flags::Flag2);
assert_eq!(*mask, 0x000000F1);

mask.unset(Flags::Flag1);
assert_eq!(*mask, 0x000000F0);

mask.set(Flags::Flag123);
assert_eq!(*mask, 0x000008F1);

You can add meta attributes like documentation (#[doc = "..."]) to each element of the macro:

bitmask! {
    /// Doc comment for the struct
    pub mask SomeOtherMask: isize where
    /// Doc comment for the enum
    flags SomeOtherFlags {
        /// Doc comment for the flag
        FlagZero    = 0,
        FlagOne     = 1
    }
}

Maybe not the best example but still... Cake is love!

bitmask! {
    mask Cake: u8 where flags Ingredients {
        Sugar   = 0b00000001,
        Eggs    = 0b00000010,
        Flour   = 0b00000100,
        Milk    = 0b00001000
    }
}

let quality_cake = Cake::all();
assert_eq!(*quality_cake, 0b00001111);

Macros

bitmask

The macro that generates a bitmask and it's associated bit flags.