bacnet_mstp_codec/error.rs
1//! Typed failures emitted by the frame codec.
2
3use core::fmt;
4
5use crate::SinkFull;
6
7/// A frame could not be encoded or decoded.
8///
9/// Encode and decode share the variants that mean the same thing in both
10/// directions; the caller knows which one it asked for. Later releases may
11/// add variants, so a match outside this crate needs a wildcard arm.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[non_exhaustive]
14pub enum FrameError {
15 /// Decode: the input ended before the header was complete.
16 TooShort {
17 /// Octets needed for the preamble and header.
18 needed: usize,
19 /// Octets supplied.
20 available: usize,
21 },
22 /// Decode: the input does not start with the preamble `0x55, 0xFF`.
23 InvalidPreamble {
24 /// First input octet.
25 first: u8,
26 /// Second input octet.
27 second: u8,
28 },
29 /// Decode: the header CRC does not match.
30 HeaderCrcMismatch,
31 /// A reserved COBS-encoded frame type, 34-127, which the codec doesn't
32 /// implement. The header passed every other check first, source and
33 /// Length included, so it is a good header (Clause 9.5.8).
34 UnsupportedCobsFrame {
35 /// The frame type octet.
36 raw_type: u8,
37 },
38 /// The source address is 255, the broadcast address, which no station may
39 /// send from (Clause 9.3).
40 BroadcastSource,
41 /// A COBS-encoded frame's Length is outside
42 /// [`MIN_COBS_LENGTH`](crate::MIN_COBS_LENGTH)..=[`MAX_COBS_LENGTH`](crate::MAX_COBS_LENGTH),
43 /// so the header is bad (Clause 9.5.8). On encode, the data is empty or
44 /// encodes to more than the largest Length allows.
45 InvalidCobsLength {
46 /// The Length declared by the header (decode), or the one the data
47 /// would need (encode).
48 length: usize,
49 },
50 /// Decode with [`decode_frame`](crate::decode_frame): a good extended
51 /// frame header, whose COBS-encoded data can't be borrowed from the
52 /// input. It says only "decode this in place": nothing after the header
53 /// was checked, and
54 /// [`decode_frame_in_place`](crate::decode_frame_in_place) gives the
55 /// verdict on the rest of the frame.
56 ExtendedFrame {
57 /// The frame type octet, 32 or 33.
58 raw_type: u8,
59 },
60 /// The data field of a standard (not COBS-encoded) frame is longer than
61 /// one allows.
62 Oversize {
63 /// Data length declared by the header (decode) or supplied (encode).
64 length: usize,
65 /// Largest standard data field, [`crate::MAX_STANDARD_MPDU_DATA`].
66 maximum: usize,
67 },
68 /// Encode: data was supplied for a frame type that never carries any (see
69 /// [`crate::FrameType::allows_data`]).
70 UnexpectedData {
71 /// The frame type octet.
72 raw_type: u8,
73 /// Length of the supplied data.
74 length: usize,
75 },
76 /// Encode: a proprietary frame (type 128-255) has fewer than the two data
77 /// octets its vendor identifier takes (Clause 9.3.11).
78 MissingVendorId {
79 /// The frame type octet.
80 raw_type: u8,
81 /// Length of the supplied data.
82 length: usize,
83 },
84 /// Decode: the input ended before the frame did: before a standard
85 /// frame's data and data CRC, or an extended frame's encoded fields, were
86 /// complete.
87 Truncated {
88 /// The Length declared by the header.
89 length: usize,
90 /// Octets supplied after the header.
91 available: usize,
92 },
93 /// Decode: the frame's data failed its check. For a standard frame the
94 /// data CRC doesn't match. For an extended frame the CRC-32K doesn't, or
95 /// a code octet in either encoded field is zero or runs past the field's
96 /// end, which Clause 9.10.3 treats as a bad CRC too.
97 DataCrcMismatch {
98 /// The Length declared by the header.
99 length: usize,
100 },
101 /// Stream decode: a candidate frame is longer than the decoder's whole
102 /// storage, so it can never be buffered. See [`crate::StreamDecoder`].
103 FrameTooLong {
104 /// Octets the candidate needs, from its preamble to its last octet.
105 needed: usize,
106 /// The decoder's storage capacity.
107 capacity: usize,
108 },
109 /// There was no room for the octets. It comes from three places:
110 ///
111 /// - Encode, before writing: the sink reports a capacity, and the frame
112 /// doesn't fit it. `needed` is the frame's wire length, `available`
113 /// the sink's remaining capacity, and nothing was written.
114 /// - Encode, part way through: one of the sink's own writes failed with
115 /// a [`SinkFull`], as a growable sink's can, since it is never checked
116 /// up front. `needed` and `available` are that write's, as the sink
117 /// reported them, and the octets of the earlier writes stay in the
118 /// sink (see [`FrameSink`](crate::FrameSink)).
119 /// - Stream decode: a pushed slice is longer than the decoder's free
120 /// storage. `needed` is the slice less the octets of a frame the
121 /// decoder is skipping, `available` the decoder's capacity less what
122 /// it already buffers, and none of the slice was taken.
123 BufferExhausted {
124 /// Octets the refused call or write had to store.
125 needed: usize,
126 /// Room there was for them.
127 available: usize,
128 },
129}
130
131impl fmt::Display for FrameError {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 match self {
134 Self::TooShort { needed, available } => {
135 write!(f, "frame too short: need {needed}, have {available}")
136 }
137 Self::InvalidPreamble { first, second } => {
138 write!(f, "invalid preamble {first:#04X} {second:#04X}")
139 }
140 Self::HeaderCrcMismatch => f.write_str("header CRC mismatch"),
141 Self::UnsupportedCobsFrame { raw_type } => {
142 write!(f, "unsupported COBS frame type {raw_type}")
143 }
144 Self::BroadcastSource => f.write_str("source address is broadcast (0xFF)"),
145 Self::InvalidCobsLength { length } => {
146 write!(f, "COBS frame length {length} is out of range")
147 }
148 Self::ExtendedFrame { raw_type } => {
149 write!(f, "extended frame type {raw_type} must be decoded in place")
150 }
151 Self::Oversize { length, maximum } => {
152 write!(f, "data length {length} exceeds maximum {maximum}")
153 }
154 Self::UnexpectedData { raw_type, length } => {
155 write!(
156 f,
157 "frame type {raw_type} carries no data, got {length} octets"
158 )
159 }
160 Self::MissingVendorId { raw_type, length } => write!(
161 f,
162 "proprietary frame type {raw_type} needs a 2-octet vendor ID, got {length} octets"
163 ),
164 Self::Truncated { length, available } => {
165 write!(f, "truncated frame length {length}, available {available}")
166 }
167 Self::DataCrcMismatch { .. } => f.write_str("data CRC mismatch"),
168 Self::FrameTooLong { needed, capacity } => write!(
169 f,
170 "frame needs {needed} octets, decoder storage holds {capacity}"
171 ),
172 Self::BufferExhausted { needed, available } => {
173 write!(f, "buffer exhausted: need {needed}, have {available}")
174 }
175 }
176 }
177}
178
179impl core::error::Error for FrameError {}
180
181impl From<SinkFull> for FrameError {
182 fn from(full: SinkFull) -> Self {
183 Self::BufferExhausted {
184 needed: full.needed,
185 available: full.available,
186 }
187 }
188}