bnb/guide/errors.rs
1//! Errors — position-aware, and the streaming `Incomplete` signal.
2//!
3//! The codec's error type is [`BitError`](crate::BitError): a [`kind`](crate::ErrorKind),
4//! the absolute **bit offset** `at` where it happened, and the **field** being
5//! processed when it did (the innermost one wins, like a span). That makes a failure
6//! point at the exact place rather than just "parse error".
7//!
8//! ```
9//! use bnb::{bin, ErrorKind};
10//!
11//! #[bin(big)]
12//! #[derive(Debug)]
13//! struct Header { a: u16, b: u16 }
14//!
15//! // Only one byte: reading `a` (needs 16 bits) runs off the end at bit 0.
16//! let err = Header::decode_exact(&[0x00]).unwrap_err();
17//! assert!(matches!(err.kind, ErrorKind::UnexpectedEof { needed: 16, remaining: 8 }));
18//! assert_eq!(err.at, 0);
19//! assert_eq!(err.field, Some("a")); // the field gives the span
20//! ```
21//!
22//! The [`Display`](std::fmt::Display) impl renders all of it:
23//!
24//! ```
25//! # use bnb::bin;
26//! # #[bin(big)] #[derive(Debug)] struct Header { a: u16, b: u16 }
27//! let err = Header::decode_exact(&[0x00]).unwrap_err();
28//! assert_eq!(
29//! err.to_string(),
30//! "unexpected end of input: needed 16 bits, 8 remain at bit 0 (field `a`)",
31//! );
32//! ```
33//!
34//! # The variants you'll see
35//!
36//! [`ErrorKind`](crate::ErrorKind) is non-exhaustive; the common ones:
37//!
38//! - `UnexpectedEof { needed, remaining }` — ran off the end of a finite slice.
39//! - `TrailingBytes { remaining }` — `decode_exact` left whole bytes unconsumed.
40//! - `BadMagic { expected, found }` — a `magic` constant didn't match.
41//! - `Convert { message }` — a `try_map` converter failed.
42//! - `Incomplete { needed }` — a stream ran out mid-message (read more and retry).
43//! - `NotSeekable` / `BufferFull` / `TooWide` / `Io` — seek-on-a-stream, buffer cap,
44//! over-128-bit field, and underlying `io::Error`.
45//!
46//! ```
47//! use bnb::{bin, ErrorKind};
48//!
49//! #[bin(big)]
50//! #[derive(Debug)]
51//! struct One { v: u8 }
52//!
53//! #[bin(big, magic = 0xCAFEu16)]
54//! #[derive(Debug)]
55//! struct Framed { v: u8 }
56//!
57//! // Trailing bytes after a complete message.
58//! let err = One::decode_exact(&[0x01, 0x02]).unwrap_err();
59//! assert!(matches!(err.kind, ErrorKind::TrailingBytes { remaining: 1 }));
60//!
61//! // A magic mismatch points at the magic.
62//! let err = Framed::decode_exact(&[0x00, 0x00, 0x00]).unwrap_err();
63//! assert!(matches!(err.kind, ErrorKind::BadMagic { expected: 0xCAFE, found: 0x0000 }));
64//! assert_eq!(err.field, Some("magic"));
65//! ```
66//!
67//! # Streaming: `Incomplete` means "retry", not "fail"
68//!
69//! When a forward stream runs out partway through a message, the error is
70//! `Incomplete` — a signal to read more bytes and retry the decode, as opposed to a
71//! definitive failure. [`is_incomplete`](crate::BitError::is_incomplete) distinguishes
72//! the two:
73//!
74//! ```
75//! use bnb::{bin, StreamBitReader};
76//!
77//! #[bin(big)]
78//! #[derive(Debug)]
79//! struct Quad { v: u32 }
80//!
81//! // Only 2 of the 4 needed bytes are available so far.
82//! let mut s = StreamBitReader::new(&[0x12, 0x34][..]);
83//! let err = Quad::decode_from(&mut s).unwrap_err();
84//! assert!(err.is_incomplete()); // buffer more and try again — not a parse error
85//! ```
86//!
87//! # Two error types
88//!
89//! Decoding/encoding yields [`BitError`](crate::BitError). The separate
90//! [`Error`](crate::Error) covers *construction* (`UInt::try_new` and the `TryFrom`
91//! impls). A `From<Error> for BitError` bridges them, so a construction failure inside
92//! a custom `parse_with`/converter can `?`-propagate:
93//!
94//! ```
95//! use bnb::{u4, BitError};
96//! fn make(raw: u8) -> Result<u4, BitError> {
97//! let v = u4::try_new(raw)?; // construction Error -> BitError via `?`
98//! Ok(v)
99//! }
100//! assert!(make(3).is_ok());
101//! assert!(make(99).is_err());
102//! ```