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