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
//! `calc`/`temp` (ROADMAP Phase 2, P2.4): `#[br(temp)] #[bw(calc = …)]` reads a
//! field into a local (usable by a later `count`) but does **not** store it —
//! `#[bin]` drops it from the struct and the builder — and recomputes it on write,
//! so the on-wire length can't drift from the `Vec`.
mod macro_ {
use bnb::{bin, u4};
#[bin]
#[derive(Debug, PartialEq, Eq, Clone)]
struct Frame {
tag: u4,
#[br(temp)]
#[bw(calc = self.items.len() as u16)]
count: u16,
#[br(count = count)]
items: Vec<u8>,
}
#[test]
fn temp_field_is_dropped_and_recomputed() {
// `count` is not a field — the struct literal omits it (compile-time proof),
// and the on-wire count is recomputed from `items` on every encode.
for n in [0usize, 1, 3, 7] {
let f = Frame {
tag: u4::new(0x5),
items: vec![0xAB; n],
};
let bytes = f.to_bytes().unwrap();
let decoded = Frame::decode_exact(&bytes).unwrap();
assert_eq!(decoded, f);
// Round-trip success across n proves the calc'd count matched items.len():
// a wrong count would read the wrong number of elements.
assert_eq!(decoded.items.len(), n);
}
}
#[test]
fn builder_has_no_temp_field() {
// The builder is over the cleaned struct, so it has `tag`/`items` but no
// `count` (temp ⇒ not stored ⇒ not a builder field).
let f = Frame::builder()
.tag(u4::new(0xA))
.items(vec![0x11, 0x22])
.build()
.unwrap();
assert_eq!(Frame::decode_exact(&f.to_bytes().unwrap()).unwrap(), f);
}
}