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
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(clippy::unwrap_used)]
//! See: `book/tutorial/12-nested-packets.md` for full example
use tinyklv::prelude::*; // Klv proc-macro + traits
use tinyklv::dec::binary as decb; // binary decoders
use tinyklv::enc::binary as encb; // binary encoders
#[derive(Klv, Debug, PartialEq, Clone, Copy)]
#[klv(
stream = &[u8],
key(dec = decb::u8, enc = encb::u8),
len(dec = decb::u8_as_usize, enc = encb::u8_from_usize),
)]
/// Nested sub-packet
///
/// Sentinel **could** exist for stand-alone packet, however
/// in this example, it will never be seen as a top-level frame
struct GpsFix {
#[klv(
key = 0x01,
dec = decb::be_i32,
enc = *encb::be_i32
)]
/// Latitude in micro-degrees (i32)
lat_udeg: i32,
#[klv(
key = 0x02,
dec = decb::be_i32,
enc = *encb::be_i32
)]
/// Longitude in micro-degrees (i32)
lon_udeg: i32,
#[klv(
key = 0x03,
dec = decb::u8,
enc = *encb::u8
)]
/// Number of satellites used for the fix
satellites: u8,
}
#[derive(Klv, Debug, PartialEq)]
#[klv(
stream = &[u8],
sentinel = b"HEARTBEAT",
key(dec = decb::u8, enc = encb::u8),
len(dec = decb::u8_as_usize, enc = encb::u8_from_usize),
)]
/// Top-level heartbeat frame with a nested `GpsFix`
struct Heartbeat {
#[klv(
key = 0x01,
dec = decb::u8,
enc = *encb::u8,
)]
sequence: u8,
#[klv(
key = 0x02,
dec = decb::be_u16,
enc = *encb::be_u16,
)]
temperature_centideg: u16,
#[klv(
key = 0x03,
dec = decb::be_u32,
enc = *encb::be_u32,
)]
uptime_s: u32,
#[klv(
key = 0x04,
dec = GpsFix::decode_value,
enc = GpsFix::encode_value,
)]
/// Nested GpsFix: `dec`/`enc` to the derived methods on the inner type
gps: GpsFix,
}
fn main() {
// build a heartbeat carrying a GPS fix
let original = Heartbeat {
sequence: 42,
temperature_centideg: 2350,
uptime_s: 3600,
gps: GpsFix {
lat_udeg: 51_477_200, // ~51.477 N (London)
lon_udeg: -126_772, // ~0.127 W
satellites: 9,
},
};
// encode - the GPS sub-packet occupies the value region of key 0x04 and
// contains its own key/length/value triples inside
let frame = original.encode_frame();
// decode - outer and inner are reconstructed in one call
let decoded = Heartbeat::decode_frame(
&mut frame.as_slice(),
).unwrap();
assert_eq!(decoded, original);
// the inner type can also be used on its own, which is how we assert it
// round-trips independently of the outer frame
let inner_bytes = original.gps.encode_value();
let inner_decoded = GpsFix::decode_value(
&mut inner_bytes.as_slice(),
).unwrap();
assert_eq!(inner_decoded, original.gps);
}