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
//! **checked** — `try_map`: a fallible wire→value conversion that **rejects** an invalid byte at
//! decode time with `ErrorKind::Convert`. This is the deliberate-rejection counterpart to the
//! permissive defaults: use `try_map` for values that are genuinely *unrepresentable*, not
//! merely *unknown* (for unknown-but-valid, keep `#[catch_all]` / flag retention). It also
//! differs from construction-side `validate`, which gates `build()` but leaves the parser open.
//! (For a pure *guard* — same type in and out, no conversion — use `#[br(assert(...))]`
//! instead: no write inverse needed. See `versioned`. `try_map` earns its keep when the
//! domain type genuinely differs from the wire type, as here.)
//!
//! Run with: `cargo run -p bitsandbytes --example checked`
use bnb::{ErrorKind, bin};
/// A protocol version this build understands — only 1 and 2 exist.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum Version {
V1,
V2,
}
/// The fallible conversion: an unknown version byte is a hard error, not a retained unknown.
fn to_version(raw: u8) -> Result<Version, String> {
match raw {
1 => Ok(Version::V1),
2 => Ok(Version::V2),
other => Err(format!("unsupported version {other}")),
}
}
#[bin(big)]
#[derive(Debug, PartialEq, Eq, Clone)]
struct Header {
#[br(try_map = to_version)]
#[bw(map = |v: &Version| match v { Version::V1 => 1u8, Version::V2 => 2u8 })]
version: Version,
#[brw(count_prefix = u8)] // derived, never stored, checked at encode
#[try_str]
name: Vec<u8>,
}
fn main() {
// A valid header round-trips.
let h = Header {
version: Version::V2,
name: b"alpha".to_vec(),
};
let bytes = h.to_bytes().unwrap();
println!("encoded: {bytes:02x?}");
assert_eq!(Header::decode_exact(&bytes).unwrap(), h);
println!("{h:#?}");
// A wire version of 9 isn't representable — `try_map` rejects it at decode time.
let bad = [0x09, 0x00];
let err = Header::decode_exact(&bad).unwrap_err();
println!("decoding version=9 -> {err}");
assert!(matches!(err.kind, ErrorKind::Convert { .. }));
assert_eq!(err.field, Some("version"));
println!("all checks passed");
}