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
//! **versioned** — `#[bin]` `if` driven by a **version** field (a different driver than
//! `conditional`'s per-bit flags): a v2 message carries fields a v1 message doesn't, so old and
//! new peers share one wire type. The version is guarded by `#[br(assert(...))]`, so an
//! unknown version is rejected at decode — a pure read-side guard, no write inverse needed.
//!
//! Run with: `cargo run -p bitsandbytes --example versioned`
use bnb::{ErrorKind, bin};
/// A length-prefixed label (v2 only).
#[bin(big)]
#[derive(Debug, PartialEq, Eq, Clone)]
struct Label {
#[brw(count_prefix = u8)] // derived, never stored, checked at encode
#[try_str]
text: Vec<u8>,
}
#[bin(big)]
#[derive(Debug, PartialEq, Eq, Clone)]
struct Event {
// The decode-time guard: reject a version this build doesn't speak. Read-only —
// encode still writes whatever is stored (you can forge what you wouldn't accept).
#[br(assert((1..=2).contains(&version), "unsupported version {}", version))]
version: u8,
id: u32,
// v2 extended the event with a priority and a label; a v1 event stops after `id`.
#[br(if(version >= 2))]
priority: Option<u8>,
#[br(if(version >= 2))]
label: Option<Label>,
}
fn main() {
// A v2 event carries the extra fields.
let v2 = Event {
version: 2,
id: 1001,
priority: Some(5),
label: Some(Label {
text: b"deploy".to_vec(),
}),
};
let bytes = v2.to_bytes().unwrap();
println!("v2 event: {:>2} bytes {bytes:02x?}", bytes.len());
assert_eq!(Event::decode_exact(&bytes).unwrap(), v2);
println!("{v2:#?}");
// A v1 event omits them entirely — they aren't on the wire.
let v1 = Event {
version: 1,
id: 1002,
priority: None,
label: None,
};
let bytes = v1.to_bytes().unwrap();
println!("v1 event: {:>2} bytes {bytes:02x?}", bytes.len());
assert_eq!(Event::decode_exact(&bytes).unwrap(), v1);
// An unknown version is rejected at decode — `try_map` fails before any later field is read.
let err = Event::decode_exact(&[0x09, 0x00, 0x00, 0x03, 0xEA]).unwrap_err();
println!("decoding version=9 -> {err}");
assert!(matches!(err.kind, ErrorKind::Convert { .. }));
assert_eq!(err.field, Some("version"));
println!("all checks passed");
}