1#[cfg(not(feature = "std"))]
8use alloc::string::String;
9#[cfg(feature = "std")]
10use std::time::Duration;
11
12#[derive(Debug, thiserror::Error)]
14pub enum Error {
15 #[error("BACnet error: class={class} code={code}")]
17 Protocol {
18 class: u32,
20 code: u32,
22 },
23
24 #[error("BACnet reject: reason={reason}")]
26 Reject {
27 reason: u8,
29 },
30
31 #[error("BACnet abort: reason={reason}")]
33 Abort {
34 reason: u8,
36 },
37
38 #[error("encoding error: {0}")]
40 Encoding(String),
41
42 #[error("decoding error at offset {offset}: {message}")]
44 Decoding {
45 offset: usize,
47 message: String,
49 },
50
51 #[cfg(feature = "std")]
53 #[error("transport error: {0}")]
54 Transport(#[from] std::io::Error),
55
56 #[cfg(feature = "std")]
58 #[error("request timed out after {0:?}")]
59 Timeout(Duration),
60
61 #[error("segmentation error: {0}")]
63 Segmentation(String),
64
65 #[error("buffer too short: need {need} bytes, have {have}")]
67 BufferTooShort {
68 need: usize,
70 have: usize,
72 },
73
74 #[error("invalid tag: {0}")]
76 InvalidTag(String),
77
78 #[error("value out of range: {0}")]
80 OutOfRange(String),
81}
82
83pub type Result<T> = core::result::Result<T, Error>;
85
86impl Error {
87 pub fn decoding(offset: usize, message: impl Into<String>) -> Self {
89 Self::Decoding {
90 offset,
91 message: message.into(),
92 }
93 }
94
95 pub fn buffer_too_short(need: usize, have: usize) -> Self {
97 Self::BufferTooShort { need, have }
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn protocol_error_display() {
107 let err = Error::Protocol { class: 2, code: 31 };
108 assert!(err.to_string().contains("class=2"));
109 assert!(err.to_string().contains("code=31"));
110 }
111
112 #[test]
113 fn decoding_error_display() {
114 let err = Error::decoding(42, "unexpected tag");
115 assert!(err.to_string().contains("offset 42"));
116 assert!(err.to_string().contains("unexpected tag"));
117 }
118
119 #[test]
120 fn buffer_too_short_display() {
121 let err = Error::buffer_too_short(10, 3);
122 assert!(err.to_string().contains("need 10"));
123 assert!(err.to_string().contains("have 3"));
124 }
125
126 #[cfg(feature = "std")]
127 #[test]
128 fn timeout_error_display() {
129 let err = Error::Timeout(Duration::from_secs(3));
130 assert!(err.to_string().contains("3s"));
131 }
132}