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
//! **enums** — `#[derive(BitEnum)]` in depth: the `num_enum` / `bitbybit::bitenum` replacement.
//! Exhaustive enums, catch-all (dual-use) enums, nesting in a bitfield, and checked integer
//! construction with error handling.
//!
//! Run with: `cargo run -p bitsandbytes --example enums`
use bnb::{BitEnum, Bits, bitfield, u2, u4};
// An exhaustive 2-bit enum: all four values named, so no catch-all is needed.
#[derive(BitEnum, Clone, Copy, Debug, PartialEq, Eq)]
#[bit_enum(u2)]
enum Ecn {
NotEct,
Ect1,
Ect0,
Ce,
}
// An ARP-style hardware-type enum: a catch-all preserves the long tail of IANA
// values we don't name (exactly the `num_enum(catch_all)` pattern). Because the
// width is byte-aligned, the derive also emits `From<HardwareType> for u16` and,
// thanks to the catch-all, an infallible `From<u16> for HardwareType` — so this
// one derive replaces a hand-written magic-byte enum *and* its
// `impl From<…> for u16` *and* the round-trips-every-discriminant test.
#[derive(BitEnum, Clone, Copy, Debug, PartialEq, Eq)]
#[bit_enum(u16, bytes = big)]
#[repr(u16)]
enum HardwareType {
Ethernet = 1,
Ieee802 = 6,
FrameRelay = 15,
InfiniBand = 32,
#[catch_all]
Other(u16),
}
// A closed set: no `#[catch_all]`, and the two values don't cover all of `u8`, so
// `closed` asserts the set is intentionally closed. The derive then emits a *checked*
// `TryFrom<u8>` that rejects unknowns with a `bnb::UnknownDiscriminant`. (Without
// `closed` this is a compile error, because the infallible codec path would panic on
// an unknown discriminant — `closed` is the explicit acknowledgement of that.)
#[derive(BitEnum, Clone, Copy, Debug, PartialEq, Eq)]
#[bit_enum(u8, closed)]
#[repr(u8)]
enum Direction {
Request = 1,
Reply = 2,
}
// A 4-bit catch-all enum nested in an 8-bit bitfield.
#[derive(BitEnum, Clone, Copy, Debug, PartialEq, Eq)]
#[bit_enum(u4)]
enum Op {
Read,
Write,
#[catch_all]
Vendor(u4),
}
#[bitfield(u8, bits = msb)]
#[derive(Clone, Copy, Debug)]
struct Command {
op: Op,
flags: u4,
}
fn main() {
// Exhaustive: every value maps to a named variant.
for raw in 0u128..4 {
println!("ecn {raw} -> {:?}", Ecn::from_bits(raw));
}
// Catch-all: known values are named, unknown ones preserved losslessly.
println!("hw 1 -> {:?}", HardwareType::from_bits(1)); // Ethernet
println!("hw 99 -> {:?}", HardwareType::from_bits(99)); // Other(99)
assert_eq!(HardwareType::from_bits(99), HardwareType::Other(99));
assert_eq!(HardwareType::Other(99).into_bits(), 99); // round-trips
// The num_enum-style `From` parity (byte-aligned + catch-all => both ways
// infallible). No hand-written `impl From<…> for u16`, no round-trip test.
assert_eq!(u16::from(HardwareType::Ethernet), 1);
assert_eq!(HardwareType::from(99u16), HardwareType::Other(99));
assert_eq!(u16::from(HardwareType::from(0x1234u16)), 0x1234); // total round-trip
// No catch-all => a checked `TryFrom`, not a panic-on-unknown.
assert_eq!(Direction::try_from(2u8), Ok(Direction::Reply));
match Direction::try_from(7u8) {
Ok(d) => println!("direction: {d:?}"),
Err(e) => println!("rejected: {e}"), // "Direction has no variant for discriminant 7"
}
assert!(Direction::try_from(7u8).is_err());
// Nested in a bitfield.
let cmd = Command::new()
.with_op(Op::Vendor(u4::new(0xC)))
.with_flags(u4::new(0x5));
println!(
"command byte: {:#04x} (op={:?}, flags={})",
cmd.to_raw(),
cmd.op(),
cmd.flags()
);
assert_eq!(cmd.op(), Op::Vendor(u4::new(0xC)));
// Checked integer construction — errors instead of panicking.
match u2::try_new(5) {
Ok(v) => println!("ok: {v}"),
Err(e) => println!("rejected: {e}"), // "value 5 does not fit in 2 bits"
}
assert!(u2::try_new(3).is_ok());
assert!(u2::try_new(4).is_err());
}