diff --git a/README.md b/README.md index 8197a26..7d9538c 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,63 @@ Add this to your `Cargo.toml`: bitfield-struct = "0.3" ``` -## Example +## Basics + +Let's begin with a simple example. +Suppose we want to store multiple data inside a single Byte, as shown below: + + + + + + + + + + + + + + + + + + +
76543310
PLevelSKind
+ +This crate generates a nice wrapper type that makes it easy to do this: -The example below shows the main features of the macro and how to use them. +```rust +/// Define your type like this with the bitfield attribute +#[bitfield(u8)] +struct MyByte { + /// The first field occupies the least significant bits + #[bits(4)] + kind: usize, + /// Booleans are 1 bit large + system: bool, + /// The bits attribute specifies the bit size of this field + #[bits(2)] + level: usize, + /// The last field spans over the most significant bits + present: bool +} +// The macro creates three accessor functions for each field: +// , with_ and set_ +let my_byte = MyByte::new() + .with_kind(15) + .with_system(false) + .with_level(3) + .with_present(true); + +assert!(my_byte.present()); +``` + +## Features + +Additionally, this crate has a few useful features, which are shown here in more detail. + +The example below shows how attributes are carried over and how signed integers, padding, and custom types are handled. ```rust /// A test bitfield with documentation diff --git a/src/lib.rs b/src/lib.rs index 2c756ab..1570cbe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,9 +3,72 @@ //! Procedural macro for bitfields that allows specifying bitfields as structs. //! As this library provides a procedural-macro it has no runtime dependencies and works for `no-std`. //! -//! ## Example +//! - Supports bool flags, raw integers, and every custom type convertible into integers (structs/enums) +//! - Ideal for driver/OS/embedded development (defining HW registers/structures) +//! - Generates minimalistic, pure, safe rust functions +//! - Compile-time checks for type and field sizes +//! - Rust-analyzer friendly (carries over documentation to accessor functions) +//! - Exports field offsets and sizes as constants (useful for const asserts) +//! - Generation of `fmt::Debug` //! -//! The example below shows the main features of the macro and how to use them. +//! ## Basics +//! +//! Let's begin with a simple example.
+//! Suppose we want to store multiple data inside a single Byte, as shown below: +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//!
76543310
PLevelSKind
+//! +//! This crate is able to generate a nice wrapper type that makes it easy to do this: +//! +//! ``` +//! # use bitfield_struct::bitfield; +//! /// Define your type like this with the bitfield attribute +//! #[bitfield(u8)] +//! struct MyByte { +//! /// The first field occupies the least significant bits +//! #[bits(4)] +//! kind: usize, +//! /// Booleans are 1 bit large +//! system: bool, +//! /// The bits attribute specifies the bit size of this field +//! #[bits(2)] +//! level: usize, +//! /// The last field spans over the most significant bits +//! present: bool +//! } +//! // The macro creates three accessor functions for each field: +//! // , with_ and set_ +//! let my_byte = MyByte::new() +//! .with_kind(15) +//! .with_system(false) +//! .with_level(3) +//! .with_present(true); +//! +//! assert!(my_byte.present()); +//! ``` +//! +//! ## Features +//! +//! Additionally, this crate has a few useful features, which are shown here in more detail. +//! +//! The example below shows how attributes are carried over and how signed integers, padding, and custom types are handled. //! //! ``` //! # use bitfield_struct::bitfield; @@ -439,7 +502,7 @@ fn bits(attrs: &[syn::Attribute], ty: &syn::Type) -> syn::Result<(TypeClass, usi if bits <= size { Ok((class, bits)) } else { - Err(syn::Error::new(tokens.span(), "overflowing member type")) + Err(syn::Error::new(tokens.span(), "overflowing field type")) } } else { Ok((TypeClass::Other, bits)) diff --git a/tests/test.rs b/tests/test.rs index 95fbafb..a590979 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2,71 +2,58 @@ use std::fmt; use bitfield_struct::bitfield; -/// A test bitfield with documentation -#[bitfield(u64)] -struct MyBitfield { - /// defaults to 16 bits for u16 - int: u16, - /// interpreted as 1 bit flag - flag: bool, - /// custom bit size - #[bits(1)] - tiny: u8, - /// sign extend for signed integers - #[bits(13)] - negative: i16, - /// supports any type that implements `From` and `Into` - #[bits(16)] - custom: CustomEnum, - /// public field -> public accessor functions - #[bits(12)] - pub public: usize, - /// padding - #[bits(5)] - _p: u8, - /// zero-sized members are ignored - #[bits(0)] - _completely_ignored: String, -} +#[test] +fn members() { + /// A test bitfield with documentation + #[bitfield(u64)] + struct MyBitfield { + /// defaults to 16 bits for u16 + int: u16, + /// interpreted as 1 bit flag + flag: bool, + /// custom bit size + #[bits(1)] + tiny: u8, + /// sign extend for signed integers + #[bits(13)] + negative: i16, + /// supports any type that implements `From` and `Into` + #[bits(16)] + custom: CustomEnum, + /// public field -> public accessor functions + #[bits(12)] + pub public: usize, + /// padding + #[bits(5)] + _p: u8, + /// zero-sized members are ignored + #[bits(0)] + _completely_ignored: String, + } -/// A custom enum -#[derive(Debug, PartialEq, Eq)] -#[repr(u64)] -enum CustomEnum { - A = 0, - B = 1, - C = 2, -} -impl From for CustomEnum { - fn from(value: u64) -> Self { - match value { - 0 => Self::A, - 1 => Self::B, - _ => Self::C, - } + /// A custom enum + #[derive(Debug, PartialEq, Eq)] + #[repr(u64)] + enum CustomEnum { + A = 0, + B = 1, + C = 2, } -} -impl From for u64 { - fn from(value: CustomEnum) -> Self { - value as _ + impl From for CustomEnum { + fn from(value: u64) -> Self { + match value { + 0 => Self::A, + 1 => Self::B, + _ => Self::C, + } + } } -} - -/// We have a custom debug implementation -> opt out -#[bitfield(u64, debug = false)] -#[derive(PartialEq, Eq, Default)] -struct Full { - data: u64, -} - -impl fmt::Debug for Full { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "0x{:x}", self.data()) + impl From for u64 { + fn from(value: CustomEnum) -> Self { + value as _ + } } -} -#[test] -fn members() { let mut val = MyBitfield::new() .with_int(3 << 15) .with_flag(true) @@ -100,8 +87,15 @@ fn members() { #[test] fn attrs() { + /// We have a custom debug implementation -> opt out + #[bitfield(u64)] + #[derive(PartialEq, Eq, Default)] + struct Full { + data: u64, + } + let full = Full::default(); - assert_eq!(full, Full::new()); + assert_eq!(full.0, Full::new().0); let full = Full::new().with_data(u64::MAX); assert_eq!(full.data(), u64::MAX); @@ -110,6 +104,18 @@ fn attrs() { #[test] fn debug() { - let full = Full::default(); + /// We have a custom debug implementation -> opt out + #[bitfield(u64, debug = false)] + struct Full { + data: u64, + } + + impl fmt::Debug for Full { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "0x{:x}", self.data()) + } + } + + let full = Full::new().with_data(123); println!("{full:?}"); }