Skip to main content

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}