Skip to content

Commit

Permalink
feat(bitfield): add enum_from_bits! macro (#450)
Browse files Browse the repository at this point in the history
This branch adds a macro to the `mycelium-bitfield` crate that allows
automatically generating `FromBits` implementations for `enum` types
that are `repr(u{N})`. For example, we can now write:

```rust
mycelium_bitfield::enum_from_bits! {
    #[derive(Debug, Eq, PartialEq)]
    pub enum MyGeneratedEnum<u8> {
        /// Isn't this cool?
        Wow = 0b1001,
        /// It sure is! :D
        Whoa = 0b0110,
    }
}
```

This probably _should_ be a `derive` proc-macro but I'm having so much
fun doing `macro_rules!` crimes in the bitfield crate for whatever
reason...

Closes #443
  • Loading branch information
hawkw authored Jul 22, 2023
1 parent 6b524bb commit ab096b4
Show file tree
Hide file tree
Showing 4 changed files with 494 additions and 137 deletions.
28 changes: 25 additions & 3 deletions bitfield/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,14 @@ and the code it generates.
#### `FromBits` trait

The [`FromBits`] trait can be implemented for user-defined types which can be
used as subfields of a [`bitfield!`]-generated structured bitfield type.
used as subfields of a [`bitfield!`]-generated structured bitfield type. This
trait may be manually implemented for any user-defined type that has a defined
bit representation, or generated automatically for `enum` types using the
[`enum_from_bits!`] macro.

For example:
```rust
use mycelium_bitfield::{bitfield, FromBits};
use mycelium_bitfield::{bitfield, enum_from_bits, FromBits};

// An enum type can implement the `FromBits` trait if it has a
// `#[repr(uN)]` attribute.
Expand Down Expand Up @@ -239,6 +242,19 @@ impl FromBits<u32> for MyEnum {
}
}

// Alternatively, the `enum_from_bits!` macro can be used to
// automatically generate a `FromBits` implementation for an
// enum type:
enum_from_bits! {
#[derive(Debug, Eq, PartialEq)]
pub enum MyGeneratedEnum<u8> {
/// Isn't this cool?
Wow = 0b1001,
/// It sure is! :D
Whoa = 0b0110,
}
}

bitfield! {
pub struct TypedBitfield<u32> {
/// Use the first two bits to represent a typed `MyEnum` value.
Expand All @@ -256,16 +272,20 @@ bitfield! {
/// `FromBits` is also implemented by (signed and unsigned) integer
/// types. This will allow the next 8 bits to be treated as a `u8`.
pub const A_BYTE: u8;

/// We can also use the automatically generated enum:
pub const OTHER_ENUM: MyGeneratedEnum;
}
}

// Unpacking a typed value with `get` will return that value, or panic if
// the bit pattern is invalid:
let my_bitfield = TypedBitfield::from_bits(0b0011_0101_1001_1110);
let my_bitfield = TypedBitfield::from_bits(0b0010_0100_0011_0101_1001_1110);

assert_eq!(my_bitfield.get(TypedBitfield::ENUM_VALUE), MyEnum::Baz);
assert_eq!(my_bitfield.get(TypedBitfield::FLAG_1), true);
assert_eq!(my_bitfield.get(TypedBitfield::FLAG_2), false);
assert_eq!(my_bitfield.get(TypedBitfield::OTHER_ENUM), MyGeneratedEnum::Wow);

// The `try_get` method will return an error rather than panicking if an
// invalid bit pattern is encountered:
Expand All @@ -289,6 +309,8 @@ implementing [`FromBits`] for user-defined types.
[bitflags-macro]: https://docs.rs/bitflags/latest/bitflags/macro.bitflags.html
[`FromBits`]:
https://docs.rs/mycelium-bitfield/latest/mycelium_bitfield/trait.FromBits.html
[`enum_from_bits!`]:
https://docs.rs/mycelium-bitfield/latest/mycelium_bitfield/macro.enum_from_bits.html
[mbf-validation]:
https://docs.rs/modular-bitfield/latest/modular_bitfield/#example-extra-safety-guard
[`Pack64`]:
Expand Down
28 changes: 25 additions & 3 deletions bitfield/src/bitfield.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,11 @@
/// ```
///
/// Bitfields may also contain typed values, as long as those values implement
/// the [`FromBits`] trait:
/// the [`FromBits`] trait. [`FromBits`] may be manually implemented, or
/// generated automatically for `enum` types using the [`enum_from_bits!`] macro:
///
/// ```
/// use mycelium_bitfield::{bitfield, FromBits};
/// use mycelium_bitfield::{bitfield, enum_from_bits, FromBits};
///
/// // An enum type can implement the `FromBits` trait if it has a
/// // `#[repr(uN)]` attribute.
Expand Down Expand Up @@ -173,6 +174,22 @@
/// }
/// }
///
/// // Alternatively, the `enum_from_bits!` macro can be used to
/// // automatically generate a `FromBits` implementation for an
/// // enum type.
/// //
/// // The macro will generate very similar code to the example
/// // manual implementation above.
/// enum_from_bits! {
/// #[derive(Debug, Eq, PartialEq)]
/// pub enum MyGeneratedEnum<u8> {
/// /// Isn't this cool?
/// Wow = 0b1001,
/// /// It sure is! :D
/// Whoa = 0b0110,
/// }
/// }
///
/// bitfield! {
/// pub struct TypedBitfield<u32> {
/// /// Use the first two bits to represent a typed `MyEnum` value.
Expand All @@ -190,16 +207,20 @@
/// /// `FromBits` is also implemented by (signed and unsigned) integer
/// /// types. This will allow the next 8 bits to be treated as a `u8`.
/// pub const A_BYTE: u8;
///
/// /// We can also use the automatically generated enum:
/// pub const OTHER_ENUM: MyGeneratedEnum;
/// }
/// }
///
/// // Unpacking a typed value with `get` will return that value, or panic if
/// // the bit pattern is invalid:
/// let my_bitfield = TypedBitfield::from_bits(0b0011_0101_1001_1110);
/// let my_bitfield = TypedBitfield::from_bits(0b0010_0100_0011_0101_1001_1110);
///
/// assert_eq!(my_bitfield.get(TypedBitfield::ENUM_VALUE), MyEnum::Baz);
/// assert_eq!(my_bitfield.get(TypedBitfield::FLAG_1), true);
/// assert_eq!(my_bitfield.get(TypedBitfield::FLAG_2), false);
/// assert_eq!(my_bitfield.get(TypedBitfield::OTHER_ENUM), MyGeneratedEnum::Wow);
///
/// // The `try_get` method will return an error rather than panicking if an
/// // invalid bit pattern is encountered:
Expand Down Expand Up @@ -334,6 +355,7 @@
/// [`example`]: crate::example
/// [`ExampleBitfield`]: crate::example::ExampleBitfield
/// [`FromBits`]: crate::FromBits
/// [`enum_from_bits!`]: crate::enum_from_bits!
#[macro_export]
macro_rules! bitfield {
(
Expand Down
Loading

0 comments on commit ab096b4

Please sign in to comment.