bacnet_bip_codec/error.rs
1//! Typed failures emitted by the BVLL codec.
2
3use core::fmt;
4
5use crate::{BvlcFunction, SinkFull};
6
7/// A BVLL message could not be decoded or encoded.
8///
9/// Each variant names the direction it comes from. None of them allocates,
10/// so the type is `Copy`. Later releases may add variants, so a match
11/// outside this crate needs a wildcard arm.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[non_exhaustive]
14pub enum BvllError {
15 /// Decode: the datagram is shorter than the 4-octet BVLL header.
16 TooShort {
17 /// Octets in the datagram.
18 length: usize,
19 },
20 /// Decode: the BVLC Type octet is not 0x81, so the datagram is not a
21 /// B/IP BVLL message (Annex J.2).
22 NotBip {
23 /// The type octet received.
24 bvlc_type: u8,
25 },
26 /// Decode: the Length field disagrees with the datagram. It counts the
27 /// whole message, header included (Annex J.2), so a datagram cut short
28 /// and one with octets after the message both fail here.
29 LengthMismatch {
30 /// Length the header declares.
31 declared: usize,
32 /// Octets in the datagram.
33 received: usize,
34 },
35 /// Decode: a function code Annex J.2 doesn't define, so its payload has
36 /// no known layout.
37 UnknownFunction {
38 /// The function octet.
39 function: u8,
40 },
41 /// Decode: the payload doesn't fit its function's layout (see
42 /// [`BvlcFunction::payload_shape`]).
43 PayloadLength {
44 /// The message's function.
45 function: BvlcFunction,
46 /// Octets after the header.
47 length: usize,
48 },
49 /// A table's octets are not a whole number of entries.
50 TableLength {
51 /// Octets supplied.
52 length: usize,
53 /// Octets per entry.
54 entry_length: usize,
55 },
56 /// A B/IP address that is not 6 octets long (Annex J.1.2).
57 AddressLength {
58 /// Octets supplied.
59 length: usize,
60 },
61 /// Encode: the message is longer than its 16-bit Length field can state.
62 TooLong {
63 /// Octets the message would take.
64 length: usize,
65 },
66 /// Encode: a BVLC-Result whose code Annex J.2.1.1 doesn't define.
67 UndefinedResultCode {
68 /// The code supplied.
69 code: u16,
70 },
71 /// Encode: the sink has no room for the message. Nothing was written to
72 /// a sink with a fixed capacity.
73 BufferExhausted {
74 /// Octets the write needed.
75 needed: usize,
76 /// Room the sink had.
77 available: usize,
78 },
79}
80
81impl fmt::Display for BvllError {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 match self {
84 Self::TooShort { length } => {
85 write!(
86 f,
87 "BVLL datagram too short: {length} octets, header needs 4"
88 )
89 }
90 Self::NotBip { bvlc_type } => {
91 write!(f, "BVLC type {bvlc_type:#04X} is not B/IP (0x81)")
92 }
93 Self::LengthMismatch { declared, received } => write!(
94 f,
95 "BVLC length {declared} does not match the {received}-octet datagram"
96 ),
97 Self::UnknownFunction { function } => {
98 write!(f, "unknown BVLC function {function:#04X}")
99 }
100 Self::PayloadLength { function, length } => {
101 write!(f, "{function} cannot carry a {length}-octet payload")
102 }
103 Self::TableLength {
104 length,
105 entry_length,
106 } => write!(
107 f,
108 "table of {length} octets is not a whole number of {entry_length}-octet entries"
109 ),
110 Self::AddressLength { length } => {
111 write!(f, "B/IP address must be 6 octets, got {length}")
112 }
113 Self::TooLong { length } => write!(
114 f,
115 "BVLL message of {length} octets exceeds the 16-bit BVLC length field"
116 ),
117 Self::UndefinedResultCode { code } => {
118 write!(f, "BVLC result code {code:#06X} is not defined")
119 }
120 Self::BufferExhausted { needed, available } => {
121 write!(f, "buffer exhausted: need {needed}, have {available}")
122 }
123 }
124 }
125}
126
127impl core::error::Error for BvllError {}
128
129impl From<SinkFull> for BvllError {
130 fn from(full: SinkFull) -> Self {
131 Self::BufferExhausted {
132 needed: full.needed,
133 available: full.available,
134 }
135 }
136}