5 releases

0.2.1 Dec 29, 2020
0.2.0 Dec 29, 2020
0.1.2 Dec 29, 2020
0.1.1 Dec 29, 2020
0.1.0 Dec 29, 2020

#22 in #bit-flags


Used in 2 crates (via flag-mast)

MIT/Apache

10KB
295 lines

flag-mast

Ergonomic Rust bit flags

Crates.io Docs.rs GitHub top language

Usage

The flag-mast crate provides a derive macro to help create ergonomic C-compatible bit flags.

Example

use flag_mast::*;

#[derive(Flags, Default)]
#[flag(name = "BARKS", method_name = "can_bark", value = 0x1)]
#[flag(name = "SITS", method_name = "can_sit", value = 0x2)]
#[flag(name = "BROWN", method_name = "is_brown", value = 0x4)]
struct Dog(#[flag_backing_field] u32);

fn foo() {
  let mut dog = Dog::default();
  dog.set_can_bark(true)
     .set_can_sit(false)
     .set_is_brown(true);
	 
  if dog.is_brown() {
    println!("Cute doggo!");
  } else {
    println!("Doggo is not brown, but is cute anyway");
  }
}

The derive macro does not change the underlying layout of the struct, it can even be repr(C)!.

The name argument is the canonical name of the flag, which need not be a valid Rust identifier or follow idiomatic Rust naming conventions.

If the name argument is appropriate as a Rust identifier (and you don't want to customize or prefix it), the method_name argument can be ommitted.

The value argument can either be an integer literal or a string containing an expression which resolves to the value for the flag.

The value needn't have to have the same exact type as the backing field, it only has to be castable to that type.

The flag_backing_field attribute specifies which field of the struct is used to hold the bit flags.

This means we can also have

use flag_mast::*;

const BLUE: u8 = 0x1;
const RED: u8 = 0x2;

mod secondary_colours {
  pub const YELLOW: u16 = 0x4;
}


#[derive(Flags, Default)]
#[flag(name = "blue", value = "BLUE")]
#[flag(name = "red", value = "RED")]
#[flag(name = "yellow", value = "secondary_colours::YELLOW")]
#[flag(name = "purple", value = "BLUE & RED")]
#[flag(name = "black", value = 0x8)]
#[repr(C)]
struct Colour{
  is_useful: bool,
  #[flag_backing_field] 
  flags: u32
}

fn bar() {
  let mut colour = Colour::default();
  colour.set_blue(true);
  colour.set_red(true);
  
  if colour.purple() {
    println("That's red AND blue!");
  }
}

Automatic Debug Implementation

The derive macro can also automatically generate a Debug implementation for you in a way that makes sense for your flags. This behaviour is controlled by an additional attribute.

use flag_mast::*;

#[derive(Flags, Default)]
#[flag_debug]
#[flag(name = "one", value = 4)]
#[flag(name = "second", method_name = "two", value = 8)]
#[flag(name = "three", value = 16)]
struct Buttons(#[flag_backing_field] u16)

fn baz() {
  let mut buttons = Buttons::default();
  buttons.set_one(true);
  
  println!("{:?}", buttons);
  println!("---");
  println!("{:#?}", buttons);
}

This will print (note that the method name is used)

Buttons { one: true, two: false, three: true }
---
Buttons {
    one: true,
    two: false,
    three: false
}

You can also choose a (potentially) more compact debug format by specifying the compact argument to the flag_debug attribute. This format only displays the flags that are set.

use flag_mast::*;

#[derive(Flags, Default)]
#[flag_debug(compact)]
#[flag(name = "one", value = 4)]
#[flag(name = "second", method_name = "two", value = 8)]
#[flag(name = "three", value = 16)]
struct Buttons(#[flag_backing_field] u16)

fn baz() {
  let mut buttons = Buttons::default();
  buttons.set_one(true);
  buttons.set_two(true);
  
  println!("{:?}", buttons);
  println!("---");
  println!("{:#?}", buttons);
}

This will print (note that the name is used)

Buttons { "one", "second" }
---
Buttons {
    "one",
    "second",
}

License

flag-mast is dual licensed under the MIT license and the Apache 2.0 license. You may choose whichever one you prefer.

Dependencies

~1.5MB
~36K SLoC