Skip to main content

bnb_macros/
lib.rs

1//! Procedural macros for the [`bnb`](https://docs.rs/bnb) crate.
2//!
3//! - [`macro@bitfield`] — pack typed fields into one backing integer with
4//!   explicit bit and byte order.
5//! - [`macro@bitflags`] — pack named single-bit flags into one integer, with set
6//!   algebra.
7//! - [`macro@BitEnum`] — derive enum ⇄ integer with an optional catch-all.
8//! - [`macro@BitDecode`] / [`macro@BitEncode`] — the low-level read/write codec
9//!   derives (fields at arbitrary bit offsets).
10//! - [`macro@bin`] — the unified whole-message codec attribute, folding the codec
11//!   derives plus a builder (also dispatches tagged-union enums).
12//! - [`macro@BitsBuilder`] — a required-by-default builder.
13//!
14//! These are re-exported from `bnb`; depend on that crate, not this one.
15
16#![forbid(unsafe_code)]
17#![deny(missing_docs)]
18
19mod bitenum;
20mod bitfield;
21mod bitflags;
22mod bitstream;
23mod builder;
24
25use proc_macro::TokenStream;
26
27/// The path to the runtime crate as the crate currently being compiled refers to
28/// it, for generated code to reference.
29///
30/// The runtime is **published as `bitsandbytes`** but its **library is named `bnb`**
31/// (`[lib] name`); downstream consumers import it as `bnb` via
32/// `bnb = { package = "bitsandbytes" }`. Cargo links the library by its lib name
33/// (`bnb`) for any *non-renamed* reference — the crate's own tests/doctests/examples,
34/// `trybuild`'s temp crates, and an un-aliased `bitsandbytes = "…"` consumer — and by
35/// the *dependency key* for a `package = "…"`-renamed one. So:
36///
37/// - [`proc_macro_crate`] reporting the package name `bitsandbytes` (or `Itself`, or
38///   not found) ⇒ a non-renamed reference ⇒ emit the lib name `::bnb`. `lib.rs` carries
39///   `extern crate self as bnb;` so `::bnb` resolves inside the lib itself too.
40/// - any other name ⇒ a `package = "…"` rename ⇒ emit that key (`::#name`).
41pub(crate) fn bnb_path() -> proc_macro2::TokenStream {
42    use proc_macro_crate::{FoundCrate, crate_name};
43    match crate_name("bitsandbytes") {
44        // Renamed dependency: the key is the import name.
45        Ok(FoundCrate::Name(name)) if name != "bitsandbytes" => {
46            let ident = proc_macro2::Ident::new(&name, proc_macro2::Span::call_site());
47            quote::quote!(::#ident)
48        }
49        // Non-renamed (`Name("bitsandbytes")`), the crate itself, or unresolved: all
50        // link the library by its lib name `bnb`.
51        _ => quote::quote!(::bnb),
52    }
53}
54
55/// The `BitDecode`/`BitEncode` impls a `Bits` type carries so it's usable as a `#[bin]` field
56/// without a `#[nested]` marker — thin delegations to the bit read/write. Emitted by every
57/// `Bits`-producing macro (`#[bitfield]`, `#[derive(BitEnum)]`, `#[bitflags]`) for the type it
58/// generates; coheres because each is a concrete impl (no blanket).
59pub(crate) fn bits_leaf_codec_impl(
60    name: &syn::Ident,
61    bnb: &proc_macro2::TokenStream,
62) -> proc_macro2::TokenStream {
63    quote::quote! {
64        impl #bnb::__private::BitDecode for #name {
65            #[inline]
66            fn bit_decode<__S: #bnb::__private::Source>(
67                __r: &mut __S,
68            ) -> ::core::result::Result<Self, #bnb::__private::BitError> {
69                __r.read::<Self>()
70            }
71        }
72        impl #bnb::__private::BitEncode for #name {
73            #[inline]
74            fn bit_encode<__K: #bnb::__private::Sink>(
75                &self,
76                __w: &mut __K,
77            ) -> ::core::result::Result<(), #bnb::__private::BitError> {
78                __w.write(*self)
79            }
80        }
81        impl #bnb::__private::FixedBitLen for #name {
82            const BIT_LEN: u32 = <Self as #bnb::__private::Bits>::BITS;
83        }
84    }
85}
86
87/// Packs the annotated struct's fields into a single backing integer.
88///
89/// ```ignore
90/// #[bitfield(u16, bits = msb, bytes = big)]
91/// #[derive(Clone, Copy, Debug, PartialEq, Eq)]
92/// struct State {
93///     opcode: u5,    // first field -> high bits (msb)
94///     flags:  Flags, // a nested bitfield
95///     rcode:  RCode, // a BitEnum; last field -> low bits
96/// }
97/// ```
98///
99/// ## Attribute arguments
100///
101/// - **backing** (first, required): the storage primitive — `u8`, `u16`, `u32`,
102///   `u64`, or `u128`. Must be at least as wide as the fields.
103/// - `bits = msb | lsb` (default `msb`): whether the first declared field lands
104///   in the high or low bits.
105/// - `bytes = big | little` (default `big`): byte order of the backing integer when
106///   serialized.
107///
108/// ## Field widths
109///
110/// A field's width is, in order of precedence: an explicit `#[bits(N)]`; an
111/// explicit `#[bits(A..=B)]` range (which also fixes its absolute offset); or,
112/// by default, `<FieldType as bnb::Bits>::BITS`. Use widths/inference for
113/// automatic layout, or ranges on **every** field for fully manual layout — the
114/// two styles cannot be mixed in one struct.
115///
116/// ## Generated API
117///
118/// `new()` (all-zero; derive `Default` yourself if you want it),
119/// `with_<field>`/`set_<field>`, `<field>()`
120/// getters, `to_raw()`/`from_raw()`, `to_be_bytes()`/`to_le_bytes()`/
121/// `from_be_bytes()`/`from_le_bytes()`, and `bnb::{Bits, Bitfield}` impls.
122///
123/// See the `bnb::guide::bitfields` page for runnable examples (bit/byte order,
124/// inferred vs. ranged widths, nesting).
125#[proc_macro_attribute]
126pub fn bitfield(attr: TokenStream, item: TokenStream) -> TokenStream {
127    bitfield::expand(attr, item)
128}
129
130/// Packs named single-bit flags into one backing integer, with set algebra.
131///
132/// ```ignore
133/// #[bitflags(u8)]
134/// #[derive(Clone, Copy, Debug, PartialEq, Eq)]
135/// struct TcpFlags {
136///     fin: bool,   // bit 0 (auto, LSB-first)
137///     syn: bool,   // bit 1
138///     #[flag(5)] ack: bool, // pinned to bit 5
139/// }
140/// ```
141///
142/// ## Attribute arguments
143///
144/// - **backing** (first, required): `u8`/`u16`/`u32`/`u64`/`u128`.
145/// - `bytes = big | little` (default `big`): byte order when serialized.
146///
147/// ## Generated API
148///
149/// A `const` per flag (upper-cased: `fin` → `TcpFlags::FIN`); `empty()`/`all()`/
150/// `bits()`/`from_bits` (retains unknown bits) / `from_bits_truncate`;
151/// `contains`/`intersects`/`is_empty`/`insert`/`remove`/`toggle`/`set`;
152/// const `union`/`intersection`/`difference`/`complement` (for combination
153/// consts); per-flag `fin()`/`with_fin(bool)`/`set_fin(bool)`; `iter()`; the
154/// `| & ^ - !` (+ assign) operators; and `Bits`/`Bitfield` impls so a
155/// flag set nests in a `#[bitfield]` and serializes.
156///
157/// See the `bnb::guide::flags` page for runnable examples (set algebra, iteration,
158/// retain-vs-truncate, nesting).
159#[proc_macro_attribute]
160pub fn bitflags(attr: TokenStream, item: TokenStream) -> TokenStream {
161    bitflags::expand(attr, item)
162}
163
164/// Derives an enum ⇄ integer mapping of a fixed bit width.
165///
166/// ```ignore
167/// #[derive(BitEnum, Clone, Copy, Debug, PartialEq, Eq)]
168/// #[bit_enum(u4)]
169/// enum RCode {
170///     NoError = 0,
171///     FormErr = 1,
172///     #[catch_all]
173///     Other(u4),   // preserves unknown values (dual-use)
174/// }
175/// ```
176///
177/// `#[bit_enum(uN)]` sets the width. Exactly one `#[catch_all]` tuple variant
178/// (holding a `uN`/integer) may capture unknown discriminants. Without a catch-all
179/// the variants must cover the whole width, or the enum must be declared
180/// `#[bit_enum(uN, closed)]` to assert a closed set — otherwise it is a **compile
181/// error**, because `from_bits` (the infallible codec / `#[bitfield]`-getter path)
182/// would panic on an unknown discriminant. A `closed` enum still `unreachable!`s on
183/// that path; its checked `TryFrom` rejects unknowns instead.
184///
185/// ## Generated API
186///
187/// Always: the `bnb::{Bits, BitEnum}` impls (so the enum nests in a
188/// `#[bitfield]`). For a **primitive** width (`u8`/`u16`/`u32`/`u64`/`u128`)
189/// additionally — for `num_enum` parity:
190///
191/// - `From<Enum> for uN` (every variant maps to a value);
192/// - with `#[catch_all]`: `From<uN> for Enum` (total — unknowns absorbed);
193/// - without it: `TryFrom<uN> for Enum`, erroring with `bnb::UnknownDiscriminant` on an
194///   unknown value.
195///
196/// A non-primitive width (`u4`, or even a byte-aligned `u24`) gets none of these — such an
197/// enum is only meaningful nested in a `#[bitfield]`.
198///
199/// See the `bnb::guide::enums` page for runnable examples (catch-all, `closed`, the
200/// `num_enum` parity, and nesting).
201#[proc_macro_derive(BitEnum, attributes(bit_enum, catch_all))]
202pub fn bit_enum(item: TokenStream) -> TokenStream {
203    bitenum::expand(item)
204}
205
206/// Derives a `bnb::BitDecode` impl that reads the struct's named fields, in
207/// declaration order, from a **bit** cursor (a `bnb::BitReader`).
208///
209/// ```ignore
210/// #[derive(BitDecode, BitEncode, Copy, Clone, Debug, PartialEq, Eq)]
211/// struct GenericBurst {   // a 264-bit DMR burst, fields at bit offsets
212///     p1: u108,           // bits   0..108
213///     pattern: SyncPattern,// bits 108..156 (a 48-bit #[derive(BitEnum)])
214///     p2: u108,           // bits 156..264
215/// }
216/// ```
217///
218/// Each leaf field is read with `Source::read`, so any `bnb::Bits` type
219/// (`u1`..`u127`, `#[bitfield]`, `#[derive(BitEnum)]`) works as a field — no
220/// byte-alignment, seeks, or shift glue. A field marked **`#[nested]`** is itself
221/// a `BitDecode`/`BitEncode` message and is recursed into (a fixed one's
222/// `FixedBitLen::BIT_LEN` counts toward the parent's). `[u8; N]` payloads and
223/// `#[br(count = …)]` `Vec`s are supported; a `count`-bearing message is
224/// variable-length and so does not implement `bnb::FixedBitLen`.
225///
226/// ## Which codec? (the derive steers you)
227///
228/// | Your data | Use | Why |
229/// |---|---|---|
230/// | Fields straddle byte boundaries (e.g. `108 \| 48 \| 108` bits) | **`#[derive(BitDecode/BitEncode)]`** or **`#[bin]`** | reads at arbitrary bit offsets |
231/// | A whole message (magic/counts/`Vec`/nesting/ctx/…), bit- or byte-aligned | **`#[bin]`** | the unified codec |
232/// | A run of sub-byte fields that fills one integer | **`#[bitfield]`** | one packed word with named accessors |
233///
234/// To enforce that, the bare derive **rejects an all-byte-aligned struct** at
235/// compile time (every field a whole number of bytes ⇒ the cursor never leaves byte
236/// boundaries ⇒ `#[bin]` is the better tool). Override with the struct-level
237/// `#[bit_stream(allow_byte_aligned)]` when you really mean it.
238///
239/// See the `bnb::guide::directives` page for a runnable example of each `#[br]`/`#[bw]`
240/// directive, and `bnb::guide::bin_codec` for the `#[bin]` front-end.
241#[proc_macro_derive(
242    BitDecode,
243    attributes(bit_stream, nested, br, bw, brw, reserved, reserved_with)
244)]
245pub fn bit_decode(item: TokenStream) -> TokenStream {
246    bitstream::expand_decode(item)
247}
248
249/// Derives a `bnb::BitEncode` impl — the dual of [`macro@BitDecode`], writing
250/// the struct's named fields in order to a `bnb::BitWriter` bit cursor. Shares
251/// [`BitDecode`](macro@BitDecode)'s right-tool guard and `#[bit_stream(...)]`
252/// override.
253#[proc_macro_derive(
254    BitEncode,
255    attributes(bit_stream, nested, br, bw, brw, reserved, reserved_with)
256)]
257pub fn bit_encode(item: TokenStream) -> TokenStream {
258    bitstream::expand_encode(item)
259}
260
261/// `#[bin]` — the unified whole-message bit codec. One attribute that folds the read
262/// codec ([`BitDecode`](macro@BitDecode)), the write codec
263/// ([`BitEncode`](macro@BitEncode)), and a required-by-default builder
264/// ([`BitsBuilder`](macro@BitsBuilder)) over a struct of `bnb::Bits` fields, read and
265/// written at arbitrary bit offsets.
266///
267/// ```ignore
268/// #[bin(big, validate = Frame::check)]
269/// #[derive(Debug, PartialEq)]
270/// struct Frame {
271///     version: u4,
272///     #[builder(default)] flags: u4,
273///     #[br(temp)] #[bw(calc = self.payload.len() as u16)] len: u16,
274///     #[br(count = len)] payload: Vec<u8>,
275/// }
276/// // -> Frame::{decode, decode_all, decode_iter, peek, decode_exact},
277/// //    Frame::{to_bytes, encode}, Frame::new(..), and Frame::builder()
278/// ```
279///
280/// ## Struct-level options
281///
282/// `big` / `little` or `bytes = big|little` (byte order), `bits = msb|lsb`, `magic = <expr>` (a leading
283/// constant verified on read / emitted on write), `read_only` / `write_only`
284/// (directional), `no_builder`, `forward_only` (bound decoding to a forward `Source`),
285/// `ctx(name: Ty, …)` (context from the parent), `validate = <path>` (a soundness
286/// check run by `build()` — the parser stays permissive),
287/// `map`/`try_map`/`wire`/`try_wire` (map the whole struct to/from a wire type — see
288/// `bnb::guide::mapping`), `auto_len(<field>.<nested> = count|bytes(<source>), …)`
289/// (cross-struct `WireLen` derivation), and — on a **single-field
290/// tuple newtype** — `codec = <module>` / `codec(parse = <f>, write = <f>)` (the
291/// per-type field codec: the type's wire form is owned by the fn pair; use it as a
292/// plain field anywhere, with `#[brw(variable)]` in a fixed parent).
293///
294/// `bits = msb` and `bytes = big` are the defaults, and `big`+`msb` is the natural
295/// pairing (network byte order); the declared byte order only swaps a **byte-multiple**
296/// value that *differs* from the bit order's natural layout, never a sub-byte field. See
297/// `bnb::guide::bin_codec` § "Byte order × bit order" for the rule.
298///
299/// ## Field directives
300///
301/// `#[br]`/`#[bw]`: `count`, `ctx { … }`, `temp` + `calc`, `if(…)`,
302/// `assert(<expr>[, "fmt", args…])` (a decode-time guard over this and earlier fields —
303/// the explicit opt-in strictness escape hatch; read-only, no inverse), `map`/`try_map`
304/// (+ the inverse `bw(map)`), `parse_with`/`write_with`, `pad_before/after`,
305/// `align_before/after`, `seek = <bits>`, `restore_position`, `dbg` (trace a field as it
306/// decodes), `#[bw(auto_len = count(<field>)|bytes(<field>))]` (derive a `WireLen<T>`
307/// field from a sibling target — the primary `WireLen` workflow); `#[brw(ignore)]`
308/// (neither read nor written); `#[brw(variable)]` (the
309/// field's type is a variable-length custom codec — suppresses the parent's
310/// `FixedBitLen`); `#[brw(count_prefix = <Ty>)]`
311/// on a `Vec<_>` (the length-prefixed count sugar — generates the `temp`+`calc`+`count`
312/// triad: the prefix sizes the `Vec` on read and is recomputed, **checked**, from
313/// `len()` on write; any `Bits` prefix type incl. `uN`); `#[try_str]` (render a
314/// byte-buffer field as text in `Debug`); plus `#[reserved]` / `#[reserved_with(…)]`.
315///
316/// ## On an enum — tagged-union dispatch
317///
318/// `#[bin]` also applies to an enum, dispatching on two orthogonal concepts: a
319/// **`magic`** wire constant (a byte string or width-suffixed unsigned int, read *and*
320/// written — the discriminant, or a verified signature on a tag-variant) and a **`tag`**
321/// selector taken from `ctx` (read-only, never on the wire). With per-variant `magic`s
322/// the enum reads the discriminant and matches (a single `==` for uniform widths, or a
323/// peek-and-`starts_with` for variable-width byte strings). With `#[bin(tag = <ctx-param>)]`
324/// and variant `#[bin(tag = V)]` it dispatches on the selector and writes no discriminant;
325/// the two compose (and may even be mixed in one enum — tag priority, then magic). The
326/// "nothing matched" tail is a `#[catch_all]` (preserving the unknown discriminant) or a
327/// typed no-tag/no-magic fallback variant, not both; without either a magic enum is a
328/// closed set. An optional enum-level `magic` is a leading prefix. Variants may be unit,
329/// tuple, named, or `#[nested]`. The codec also generates `decode_as_<variant>`,
330/// `peek_variant`/`<Name>Kind`, `decode_tagged`, and a `magic()`/`tag()` accessor where
331/// the discriminant is single-valued. See the `bnb::guide::dispatch` page.
332///
333/// On a struct, `#[bin]` lowers to `#[derive(BitDecode, BitEncode, BitsBuilder)]`, which
334/// stay usable directly. See the `bnb::guide::bin_codec` page for a full walkthrough and
335/// `bnb::guide::directives` for one runnable example per directive.
336#[proc_macro_attribute]
337pub fn bin(attr: TokenStream, item: TokenStream) -> TokenStream {
338    bitstream::expand_bin(attr, item)
339}
340
341/// Generates a `derive_builder`-style builder for a struct (or, when listed in a
342/// `#[bitfield]`'s derives, for the bitfield — intercepted by `#[bitfield]`).
343///
344/// ```ignore
345/// #[bitfield(u16, bits = msb)]
346/// #[derive(BitsBuilder, Clone, Copy)]
347/// struct State {
348///     opcode: u4,            // required
349///     #[builder(default)]    // optional; 0 if unset
350///     flags: u8,
351///     rcode: RCode,          // required
352/// }
353///
354/// let s = State::builder().opcode(u4::new(2)).rcode(RCode::ServFail).build()?;
355/// ```
356///
357/// Generates `Foo::builder() -> FooBuilder`, an `Option`-tracked setter per
358/// field, and `build() -> Result<Foo, bnb::BuilderError>` that errors on the
359/// first unset **required** field. A field is optional only with
360/// `#[builder(default)]` (`Default::default()` if unset) or
361/// `#[builder(default = expr)]`. Coexists with the infallible infix `with_*`.
362///
363/// On a `#[bitfield]` struct, list `#[bitfield(...)]` **above** the `#[derive]`
364/// so it intercepts this marker.
365///
366/// See the `bnb::guide::builders` page for runnable examples (required-field errors,
367/// `default`/`default = expr`, the `#[bitfield]` intercept).
368#[proc_macro_derive(BitsBuilder, attributes(builder))]
369pub fn bits_builder(item: TokenStream) -> TokenStream {
370    builder::expand_derive(item)
371}