1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! Whole-struct wire mapping — a *logical* type that serializes via a separate *wire* type.
//!
//! [`directives`](super::directives) showed `#[br(map)]`/`#[bw(map)]` transforming one *field*
//! between its wire value and a friendlier type. The same idea applies to a **whole struct**: a
//! *logical* type whose on-the-wire form is a different *wire* type. The struct's own fields are
//! the logical data — never read or written directly; the wire type owns the bytes.
//!
//! There are two forms:
//!
//! - **Conversion-trait form** — `#[bin(wire = WireType)]`, driven by standard `From`/`TryFrom`
//! impls you write. The transitions live in named `impl` blocks — a clean home, reusable
//! anywhere in your program — and the **wire type may be variable-length**.
//! - **Closure form** — `#[bin(map = …, bw_map = …)]`, with the transitions inline in the
//! attribute. Quick for a small fixed mapping.
//!
//! # The conversion-trait form
//!
//! `#[bin(wire = WireType)]` needs `From<WireType> for Self` (decode) and `From<&Self> for
//! WireType` (encode). Decode reads `WireType` then `Self::from(wire)`; encode does
//! `WireType::from(&self)` then writes.
//!
//! ```
//! use bnb::bin;
//!
//! // The wire form: a normal #[bin] message.
//! #[bin(big)]
//! #[derive(Debug, Clone, PartialEq)]
//! struct WireColor { r: u8, g: u8, b: u8 }
//!
//! // The logical form: a packed u32, mapped to/from WireColor.
//! #[bin(wire = WireColor)]
//! #[derive(Debug, Clone, PartialEq)]
//! struct Color { rgb: u32 }
//!
//! impl From<WireColor> for Color {
//! fn from(w: WireColor) -> Self {
//! Color { rgb: (w.r as u32) << 16 | (w.g as u32) << 8 | w.b as u32 }
//! }
//! }
//! impl From<&Color> for WireColor {
//! fn from(c: &Color) -> Self {
//! WireColor { r: (c.rgb >> 16) as u8, g: (c.rgb >> 8) as u8, b: c.rgb as u8 }
//! }
//! }
//!
//! let c = Color { rgb: 0x11_22_33 };
//! assert_eq!(c.to_bytes().unwrap(), [0x11, 0x22, 0x33]); // mapped onto the wire
//! assert_eq!(Color::decode_exact(&[0x11, 0x22, 0x33]).unwrap(), c); // and back
//! let w: WireColor = (&c).into(); // …and reusable in-program
//! assert_eq!(w.r, 0x11);
//! ```
//!
//! Use `#[bin(try_wire = WireType)]` for a fallible decode (`TryFrom<WireType> for Self`, whose
//! `Error: Display`); a conversion error becomes a decode error
//! ([`ErrorKind::Convert`](crate::ErrorKind)) — the parser never panics.
//!
//! # The closure form
//!
//! When the mapping is small and you don't need a reusable `From`, put it inline:
//!
//! ```
//! use bnb::bin;
//!
//! #[bin(big)]
//! #[derive(Debug, Clone, PartialEq)]
//! struct WireTemp { biased: u8 } // stored as celsius + 40
//!
//! #[bin(
//! map = |w: WireTemp| Celsius(w.biased as i16 - 40),
//! bw_map = |c: &Celsius| WireTemp { biased: (c.0 + 40) as u8 }
//! )]
//! #[derive(Debug, Clone, PartialEq)]
//! struct Celsius(i16);
//!
//! assert_eq!(Celsius(10).to_bytes().unwrap(), [50]);
//! assert_eq!(Celsius::decode_exact(&[50]).unwrap(), Celsius(10));
//! ```
//!
//! `try_map = |w: Wire| Result<Self, E>` is the fallible closure form (the dual of `try_wire`).
//!
//! # Variable-length logical formats
//!
//! Because a mapped type does **not** auto-implement [`FixedBitLen`](crate::FixedBitLen), its
//! wire form may be variable-length — a count-driven `Vec`, a length-prefixed string, anything:
//!
//! ```
//! use bnb::bin;
//!
//! #[bin(big)]
//! #[derive(Debug, Clone, PartialEq)]
//! struct WireText { n: u8, #[br(count = n)] data: Vec<u8> } // variable-length
//!
//! #[bin(wire = WireText)]
//! #[derive(Debug, Clone, PartialEq)]
//! struct Text(String);
//! impl From<WireText> for Text {
//! fn from(w: WireText) -> Self { Text(String::from_utf8_lossy(&w.data).into_owned()) }
//! }
//! impl From<&Text> for WireText {
//! fn from(t: &Text) -> Self { WireText { n: t.0.len() as u8, data: t.0.as_bytes().to_vec() } }
//! }
//!
//! let t = Text("hi".into());
//! assert_eq!(t.to_bytes().unwrap(), [2, b'h', b'i']);
//! assert_eq!(Text::decode_exact(&[3, b'a', b'b', b'c']).unwrap(), Text("abc".into()));
//! ```
//!
//! # Generated surface and nesting
//!
//! A mapped type gets the same entry points as any `#[bin]` message — `decode`, `decode_exact`,
//! `decode_all`, `decode_iter`, `peek`, `to_bytes` — plus the [`BitDecode`](crate::BitDecode) /
//! [`BitEncode`](crate::BitEncode) impls. It nests as a field via a `count`/`ctx`/`if` directive
//! like any message; to nest a **fixed-wire** mapped type as a *plain* field, add a one-line
//! [`FixedBitLen`](crate::FixedBitLen) impl forwarding the wire type's size (it only compiles if
//! the wire really is fixed-length):
//!
//! ```
//! use bnb::bin;
//! # #[bin(big)] #[derive(Debug, Clone, PartialEq)] struct WireColor { r: u8, g: u8, b: u8 }
//! # #[bin(wire = WireColor)] #[derive(Debug, Clone, PartialEq)] struct Color { rgb: u32 }
//! # impl From<WireColor> for Color { fn from(w: WireColor) -> Self { Color { rgb: (w.r as u32) << 16 | (w.g as u32) << 8 | w.b as u32 } } }
//! # impl From<&Color> for WireColor { fn from(c: &Color) -> Self { WireColor { r: (c.rgb >> 16) as u8, g: (c.rgb >> 8) as u8, b: c.rgb as u8 } } }
//! impl bnb::FixedBitLen for Color {
//! const BIT_LEN: u32 = <WireColor as bnb::FixedBitLen>::BIT_LEN;
//! }
//!
//! #[bin(big)]
//! #[derive(Debug, PartialEq)]
//! struct Pixel { color: Color, alpha: u8 }
//!
//! let px = Pixel { color: Color { rgb: 0x01_02_03 }, alpha: 0xFF };
//! assert_eq!(px.to_bytes().unwrap(), [0x01, 0x02, 0x03, 0xFF]);
//! assert_eq!(Pixel::decode_exact(&[0x01, 0x02, 0x03, 0xFF]).unwrap(), px);
//! ```
//!
//! # Notes
//!
//! - **One direction is fine.** For the closure form, give only `map`/`try_map` (decode-only) or
//! only `bw_map` (encode-only). For the conversion-trait form, add `read_only`/`write_only`.
//! - **Byte/bit order comes from the wire type**, so `big`/`little`/`bits` on the mapped
//! struct itself don't apply, and `magic`/`ctx`/`validate` belong on the wire type.
//! - **Pick a form:** reach for `wire`/`try_wire` when you want the transitions in a named,
//! reusable place or the wire form is variable-length; reach for `map`/`bw_map` for a quick
//! inline fixed mapping.