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