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
//! **tlv** — a Type-Length-Value-style codec: a document is a `count`-driven sequence of
//! heterogeneous, self-describing records. A leading type byte (each variant's `magic`)
//! dispatches the record; its body — fixed, or length-prefixed for the variable ones — follows.
//! The "build your own extensible wire format" orchestration: enum `magic` dispatch +
//! `count_prefix` lengths (derived, never stored, checked at encode), composed into one message.
//!
//! Run with: `cargo run -p bitsandbytes --example tlv`
use bnb::bin;
/// One record. The leading byte (its `magic`) is the type tag; the body follows.
#[bin(big)]
#[derive(Debug, PartialEq, Eq, Clone)]
enum Field {
#[bin(magic = 0x01u8)]
Version(u16),
#[bin(magic = 0x02u8)]
Name {
#[brw(count_prefix = u8)] // the length-prefixed value — works in a variant too
#[try_str]
text: Vec<u8>,
},
#[bin(magic = 0x03u8)]
Ttl(u32),
#[bin(magic = 0x04u8)]
Compression(u8),
}
/// A document: a record count, then that many self-describing records.
#[bin(big)]
#[derive(Debug, PartialEq, Eq, Clone)]
struct Document {
#[brw(count_prefix = u8)]
fields: Vec<Field>,
}
fn main() {
let doc = Document {
fields: vec![
Field::Version(2),
Field::Name {
text: b"sensor-7".to_vec(),
},
Field::Ttl(3600),
Field::Compression(1),
],
};
let bytes = doc.to_bytes().unwrap();
println!("document: {} bytes {bytes:02x?}", bytes.len());
let back = Document::decode_exact(&bytes).unwrap();
assert_eq!(back, doc);
println!("{back:#?}");
println!("all checks passed");
}