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
//! The 1.0 **scope line** for serde, pinned (see `ROADMAP.md`): bnb is *not* a serde data
//! format (serde's data model has no bit widths, byte order, magic, or count — `binrw`
//! reached the same conclusion), but user-side serde derives **coexist** with `#[bin]` on
//! plain messages — one type can carry both codecs, JSON for config/logs and bnb for the wire.
//!
//! The documented boundary (verified, not snapshot-pinned — the error text belongs to
//! serde/rustc and drifts across versions):
//! - bnb's own field types (`uN`, `#[bitfield]` structs) ship no serde impls (a post-1.0
//! additive `serde` feature, if ever demanded).
mod macro_ {
use bnb::{BitEnum, bin};
use serde::{Deserialize, Serialize};
/// One type, two codecs: `#[bin]` owns the wire bytes, serde owns the data model.
#[bin(big)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct Heartbeat {
device_id: u16,
uptime_s: u32,
errors: u8,
}
#[test]
fn serde_derives_coexist_with_bin_on_a_plain_message() {
let h = Heartbeat {
device_id: 0x0A01,
uptime_s: 86_400,
errors: 2,
};
// serde path: JSON round-trip (config files, logs, HTTP APIs).
let json = serde_json::to_string(&h).unwrap();
assert_eq!(json, r#"{"device_id":2561,"uptime_s":86400,"errors":2}"#);
assert_eq!(serde_json::from_str::<Heartbeat>(&json).unwrap(), h);
// bnb path: the wire bytes, unaffected by the serde derives.
let wire = h.to_bytes().unwrap();
assert_eq!(wire, [0x0A, 0x01, 0x00, 0x01, 0x51, 0x80, 0x02]);
assert_eq!(Heartbeat::decode_exact(&wire).unwrap(), h);
}
/// A user `BitEnum` is a normal enum — serde derives apply to it like any other,
/// independent of the bnb impls the derive adds.
#[derive(BitEnum, Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[bit_enum(u8)]
#[repr(u8)]
enum Kind {
Ping = 1,
Pong = 2,
#[catch_all]
Other(u8),
}
#[bin(big)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct Tagged {
kind: Kind,
seq: u16,
}
#[test]
fn a_bit_enum_field_carries_serde_derives_too() {
let t = Tagged {
kind: Kind::Pong,
seq: 7,
};
let json = serde_json::to_string(&t).unwrap();
assert_eq!(serde_json::from_str::<Tagged>(&json).unwrap(), t);
assert_eq!(Tagged::decode_exact(&t.to_bytes().unwrap()).unwrap(), t);
// The catch-all (tuple) variant serializes as serde's usual externally-tagged form.
let o = Tagged {
kind: Kind::Other(9),
seq: 1,
};
assert_eq!(
serde_json::from_str::<Tagged>(&serde_json::to_string(&o).unwrap()).unwrap(),
o
);
}
}