macro-bits 0.1.5

A crate for performing macro-based bit manipulation.
Documentation
#[macro_export]
#[doc(hidden)]
macro_rules! check_field_mask {
    (bool, $mask:expr, $name:ident) => {
        assert!(
            $mask != 0,
            concat!("Mask for field ", stringify!($name), " is zero.")
        );
        assert!(
            ($mask as usize).is_power_of_two(),
            concat!(
                "Field ",
                stringify!($name),
                " is of type boolean, but it's mask isn't a power of two."
            )
        );
    };
    ($field_type:ty, $mask:expr, $name:ident) => {
        assert!(
            $mask != 0,
            concat!("Mask for field ", stringify!($name), " is zero.")
        );
        assert!(
            (($mask >> 1 ^ $mask) as usize).count_ones() <= 2,
            concat!("Mask for field ", stringify!($name), " is discontinous.")
        );
    };
}
#[macro_export]
#[doc(hidden)]
macro_rules! read_field {
    (bool, $representation:ty, $shifted_value:expr) => {
        $shifted_value != 0
    };
    (u8, $representation:ty, $shifted_value:expr) => {
        $shifted_value as u8
    };
    (i8, $representation:ty, $shifted_value:expr) => {
        $shifted_value as i8
    };
    (u16, $representation:ty, $shifted_value:expr) => {
        $shifted_value as u16
    };
    (i16, $representation:ty, $shifted_value:expr) => {
        $shifted_value as i16
    };
    (u32, $representation:ty, $shifted_value:expr) => {
        $shifted_value as u32
    };
    (i32, $representation:ty, $shifted_value:expr) => {
        $shifted_value as i32
    };
    (u64, $representation:ty, $shifted_value:expr) => {
        $shifted_value as u64
    };
    (i64, $representation:ty, $shifted_value:expr) => {
        $shifted_value as i64
    };
    (u128, $representation:ty, $shifted_value:expr) => {
        $shifted_value as u128
    };
    (i128, $representation:ty, $shifted_value:expr) => {
        $shifted_value as i128
    };
    ($field_type:ty, $representation:ty, $shifted_value:expr) => {
        <$field_type as From<$representation>>::from($shifted_value)
    };
}
#[macro_export]
#[doc(hidden)]
macro_rules! write_field {
    (bool, $representation:ty, $shifted_value:expr) => {
        $shifted_value as $representation
    };
    (u8, $representation:ty, $shifted_value:expr) => {
        $shifted_value as $representation
    };
    (i8, $representation:ty, $shifted_value:expr) => {
        $shifted_value as $representation
    };
    (u16, $representation:ty, $shifted_value:expr) => {
        $shifted_value as $representation
    };
    (i16, $representation:ty, $shifted_value:expr) => {
        $shifted_value as $representation
    };
    (u32, $representation:ty, $shifted_value:expr) => {
        $shifted_value as $representation
    };
    (i32, $representation:ty, $shifted_value:expr) => {
        $shifted_value as $representation
    };
    (u64, $representation:ty, $shifted_value:expr) => {
        $shifted_value as $representation
    };
    (i64, $representation:ty, $shifted_value:expr) => {
        $shifted_value as $representation
    };
    (u128, $representation:ty, $shifted_value:expr) => {
        $shifted_value as $representation
    };
    (i128, $representation:ty, $shifted_value:expr) => {
        $shifted_value as $representation
    };
    ($field_type:ty, $representation:ty, $shifted_value:expr) => {
        <$representation as From<$field_type>>::from($shifted_value)
    };
}
#[macro_export]
/// Deconstruct a bitfield.
///
/// The parameters specified get compile checked through assertions.
/// Custom types are allowed, as long as they implement conversions from and into the representation specified on the struct.
/// ```
/// use macro_bits::bitfield;
///
/// bitfield! {
///     #[derive(Debug, PartialEq)]
///     pub struct Test: u8 {
///         /// This is a doc comment.
///         pub x: u8 => 0b0000_1111,
///         pub y: bool => 0b0001_0000,
///         pub z: u16 => 0b1110_0000
///     }
/// }
/// let target = Test {
///     x: 0xf,
///     y: true,
///     z: 7
/// };
/// assert_eq!(target, Test::from_bits(0xff));
/// assert_eq!(u8::from(target), 0xff);
/// ```
macro_rules! bitfield {
    (
        $(#[$attr:meta])*
        $struct_vis:vis struct $struct_name:ident : $struct_representation:ty {
            $(
                $(#[$field_attr:meta])*
                $field_vis:vis $field_name:ident : $field_type:ty => $field_mask:expr
            ),*
        }
    ) => {
        ::macro_bits::defile::defile! {

            $(#[$attr])*
            // Autogenerated from bitfield macro.
            $struct_vis struct $struct_name {
                $(
                    $(#[$field_attr])*
                    $field_vis $field_name: $field_type
                ),*
            }
            #[allow(ineffective_bit_mask)]
            // Autogenerated from bitfield macro.
            impl $struct_name {
                // compile-time checks
                const _BITFIELD_PROOF: () = {
                    // mask overlap
                    assert!(
                        ($($field_mask)^+) == $($field_mask)|+, 
                        "Masks are overlapping."
                    );
                    // mask type checks
                    $(
                        assert!(
                            $field_mask <= <$struct_representation>::MAX, 
                            concat!("Mask for field ", 
                                stringify!($field_name), 
                                " is larger than the representation type."
                            )
                        );
                    )*
                    // field specific checks
                    $(
                        ::macro_bits::check_field_mask!($field_type, $field_mask, $field_name);
                    )*
                };
                pub fn from_bits(value: $struct_representation) -> Self {
                    $(
                        let $field_name = 
                        ::macro_bits::read_field!(
                            $field_type, 
                            $struct_representation,
                            (value & $field_mask) >> ($field_mask as usize).trailing_zeros()
                        );
                    )+
                    Self {
                        $(
                            $field_name,
                        )+
                    }
                }
                pub fn into_bits(self) -> $struct_representation {
                    let mut data: $struct_representation = 0;
                    $(
                        data |= (
                            (
                                ::macro_bits::write_field!(
                                    $field_type, 
                                    $struct_representation, 
                                    self.$field_name
                                )
                            ) << ($field_mask as usize).trailing_zeros()
                        ) & $field_mask;
                    )*
                    data
                }
            }
            ::macro_bits::generate_conversions!($struct_representation, $struct_representation, $struct_name);
        }
    };
}