Macro mycelium_bitfield::enum_from_bits

source ·
macro_rules! enum_from_bits {
    (
        $(#[$meta:meta])* $vis:vis enum $Type:ident<$uN:ident> {
            $(#[$var1_meta:meta])*
            $Variant1:ident = $value1:expr,
            $(
                $(#[$var_meta:meta])*
                $Variant:ident = $value:expr
            ),* $(,)?
        }
    ) => { ... };
    (@bigger u8, $Type:ty) => { ... };
    (@bigger u16, $Type:ty) => { ... };
    (@bigger u32, $Type:ty) => { ... };
    (@bigger u64, $Type:ty) => { ... };
    (@bigger $uN:ty, $Type:ty) => { ... };
    (@impl $uN:ty, $Type:ty, $($bigger:ty),+) => { ... };
}
Expand description

Generates automatic FromBits and core::convert::TryFrom implementations for an enum type of repr(uN), where uN is one of u8, u16, u32, u64, or u128.

This allows an enum type to be used with the bitfield! macro without requiring a manual FromBits implementation. Essentially, this macro can be thought of as being analogous to #[derive(FromBits, TryFrom)].1

§Generated Implementations

This macro will automatically generate a FromBits<uN> and a core::convert::TryFrom<uN> implementation for the defined enum type. In addition, FromBits and core::convert::TryFrom implementations for each unsized integer type larger than uN are also automatically generated. The Copy and Clone traits are also derived for the generated enum, as they are required by the FromBits implementation..

Generated enum types are [repr(uN)]].

Additional traits may be derived for the enum type, such as PartialEq, Eq, and Default. These traits are not automatically derived, as custom implementations may also be desired, depending on the use-case. For example, the Default value for am enum may not be all zeroes.

§Examples

Basic usage:

use mycelium_bitfield::FromBits;
use core::convert::TryFrom;

mycelium_bitfield::enum_from_bits! {
   /// Doc comments can be added to generated enum types.
   #[derive(Debug, PartialEq, Eq)] // additional `derive` attributes can be added
    enum Example<u8> { // generate an enum represented by a u8
        Foo = 0b0000,
        Bar = 0b0001,
        Baz = 0b1000,
        Qux = 0b0111,
    }
}

// the generated enum will implement the `FromBits` trait:
assert_eq!(Example::try_from_bits(0b1u8), Ok(Example::Bar));
assert_eq!(FromBits::<u8>::into_bits(Example::Foo), 0);

// `core::convert::TryFrom` implementations are also generated:
assert_eq!(Example::try_from(0b1000u8), Ok(Example::Baz));
assert_eq!(0b0111u32.try_into(), Ok(Example::Qux));

// invalid bit-patterns return an error:
assert!(Example::try_from_bits(0b1001u8).is_err()); // invalid bit pattern
assert!(Example::try_from_bits(0b1000_0000u8).is_err()); // too many bits

Only u8, u16, u32, u64, and u128 may be used as reprs for generated enums:

mycelium_bitfield::enum_from_bits! {
    /// This won't work. Don't do this.
    enum InvalidRepr<i32> {
        This = 0b01,
        Wont = 0b10,
        Work = 0b11,
    }
}

  1. Why Not #[derive(FromBits)]? Some readers may be curious about why this is a declarative macro, rather than a procedural #[derive] macro. The answer is…“because I felt like it lol”. This probably should be a proc-macro, since it’s essentially just deriving a trait implementation. However, one of my goals for mycelium-bitfield was to see how far I could go using only macro_rules! macros. This isn’t because I dislike procedural macros, or that I’m concerned about proc-macro compile times — I just thought it would be a fun challenge to do everything declaratively, if it was possible. And, if you do care about the potential build time impact of proc-macro dependencies, this should help. :)