1use crate::packet::suback::SubAckReasonCode;
2use crate::prelude::String;
3use crate::protocol::v5::reason_codes::ReasonCode;
4
5#[cfg(feature = "std")]
6use thiserror::Error;
7
8#[cfg(not(feature = "std"))]
9use core::fmt;
10
11#[cfg(feature = "std")]
12pub type Result<T> = std::result::Result<T, MqttError>;
13
14#[cfg(not(feature = "std"))]
15pub type Result<T> = core::result::Result<T, MqttError>;
16
17#[derive(Debug, Clone)]
18#[cfg_attr(feature = "std", derive(Error))]
19pub enum MqttError {
20 #[cfg_attr(feature = "std", error("IO error: {0}"))]
21 Io(String),
22
23 #[cfg_attr(feature = "std", error("Invalid topic name: {0}"))]
24 InvalidTopicName(String),
25
26 #[cfg_attr(feature = "std", error("Invalid topic filter: {0}"))]
27 InvalidTopicFilter(String),
28
29 #[cfg_attr(feature = "std", error("Invalid client ID: {0}"))]
30 InvalidClientId(String),
31
32 #[cfg_attr(feature = "std", error("Connection error: {0}"))]
33 ConnectionError(String),
34
35 #[cfg_attr(feature = "std", error("Connection refused: {0:?}"))]
36 ConnectionRefused(ReasonCode),
37
38 #[cfg_attr(feature = "std", error("Protocol error: {0}"))]
39 ProtocolError(String),
40
41 #[cfg_attr(feature = "std", error("Malformed packet: {0}"))]
42 MalformedPacket(String),
43
44 #[cfg_attr(
45 feature = "std",
46 error("Packet too large: size {size} exceeds maximum {max}")
47 )]
48 PacketTooLarge { size: usize, max: usize },
49
50 #[cfg_attr(feature = "std", error("Authentication failed"))]
51 AuthenticationFailed,
52
53 #[cfg_attr(feature = "std", error("Not authorized"))]
54 NotAuthorized,
55
56 #[cfg_attr(feature = "std", error("Not connected"))]
57 NotConnected,
58
59 #[cfg_attr(feature = "std", error("Already connected"))]
60 AlreadyConnected,
61
62 #[cfg_attr(feature = "std", error("Timeout"))]
63 Timeout,
64
65 #[cfg_attr(feature = "std", error("Subscription failed: {0:?}"))]
66 SubscriptionFailed(ReasonCode),
67
68 #[cfg_attr(feature = "std", error("Subscription denied: {0:?}"))]
69 SubscriptionDenied(SubAckReasonCode),
70
71 #[cfg_attr(feature = "std", error("Unsubscription failed: {0:?}"))]
72 UnsubscriptionFailed(ReasonCode),
73
74 #[cfg_attr(feature = "std", error("Publish failed: {0:?}"))]
75 PublishFailed(ReasonCode),
76
77 #[cfg_attr(feature = "std", error("Packet identifier not found: {0}"))]
78 PacketIdNotFound(u16),
79
80 #[cfg_attr(feature = "std", error("Packet identifier already in use: {0}"))]
81 PacketIdInUse(u16),
82
83 #[cfg_attr(feature = "std", error("Invalid QoS: {0}"))]
84 InvalidQoS(u8),
85
86 #[cfg_attr(feature = "std", error("Invalid packet type: {0}"))]
87 InvalidPacketType(u8),
88
89 #[cfg_attr(feature = "std", error("Invalid reason code: {0}"))]
90 InvalidReasonCode(u8),
91
92 #[cfg_attr(feature = "std", error("Invalid property ID: {0}"))]
93 InvalidPropertyId(u8),
94
95 #[cfg_attr(feature = "std", error("Duplicate property ID: {0}"))]
96 DuplicatePropertyId(u8),
97
98 #[cfg_attr(feature = "std", error("Session expired"))]
99 SessionExpired,
100
101 #[cfg_attr(feature = "std", error("Keep alive timeout"))]
102 KeepAliveTimeout,
103
104 #[cfg_attr(feature = "std", error("Server shutting down"))]
105 ServerShuttingDown,
106
107 #[cfg_attr(feature = "std", error("Client closed connection"))]
108 ClientClosed,
109
110 #[cfg_attr(feature = "std", error("Connection closed by peer"))]
111 ConnectionClosedByPeer,
112
113 #[cfg_attr(feature = "std", error("Maximum connect time exceeded"))]
114 MaxConnectTime,
115
116 #[cfg_attr(feature = "std", error("Topic alias invalid: {0}"))]
117 TopicAliasInvalid(u16),
118
119 #[cfg_attr(feature = "std", error("Receive maximum exceeded"))]
120 ReceiveMaximumExceeded,
121
122 #[cfg_attr(feature = "std", error("Will message rejected"))]
123 WillRejected,
124
125 #[cfg_attr(feature = "std", error("Implementation specific error: {0}"))]
126 ImplementationSpecific(String),
127
128 #[cfg_attr(feature = "std", error("Unsupported protocol version"))]
129 UnsupportedProtocolVersion,
130
131 #[cfg_attr(feature = "std", error("Invalid state: {0}"))]
132 InvalidState(String),
133
134 #[cfg_attr(feature = "std", error("Client identifier not valid"))]
135 ClientIdentifierNotValid,
136
137 #[cfg_attr(feature = "std", error("Bad username or password"))]
138 BadUsernameOrPassword,
139
140 #[cfg_attr(feature = "std", error("Server unavailable"))]
141 ServerUnavailable,
142
143 #[cfg_attr(feature = "std", error("Server busy"))]
144 ServerBusy,
145
146 #[cfg_attr(feature = "std", error("Banned"))]
147 Banned,
148
149 #[cfg_attr(feature = "std", error("Bad authentication method"))]
150 BadAuthenticationMethod,
151
152 #[cfg_attr(feature = "std", error("Quota exceeded"))]
153 QuotaExceeded,
154
155 #[cfg_attr(feature = "std", error("Payload format invalid"))]
156 PayloadFormatInvalid,
157
158 #[cfg_attr(feature = "std", error("Retain not supported"))]
159 RetainNotSupported,
160
161 #[cfg_attr(feature = "std", error("QoS not supported"))]
162 QoSNotSupported,
163
164 #[cfg_attr(feature = "std", error("Use another server"))]
165 UseAnotherServer,
166
167 #[cfg_attr(feature = "std", error("Server moved"))]
168 ServerMoved,
169
170 #[cfg_attr(feature = "std", error("Shared subscriptions not supported"))]
171 SharedSubscriptionsNotSupported,
172
173 #[cfg_attr(feature = "std", error("Connection rate exceeded"))]
174 ConnectionRateExceeded,
175
176 #[cfg_attr(feature = "std", error("Subscription identifiers not supported"))]
177 SubscriptionIdentifiersNotSupported,
178
179 #[cfg_attr(feature = "std", error("Wildcard subscriptions not supported"))]
180 WildcardSubscriptionsNotSupported,
181
182 #[cfg_attr(feature = "std", error("Message too large for queue"))]
183 MessageTooLarge,
184
185 #[cfg_attr(feature = "std", error("Flow control exceeded"))]
186 FlowControlExceeded,
187
188 #[cfg_attr(feature = "std", error("Packet ID exhausted"))]
189 PacketIdExhausted,
190
191 #[cfg_attr(
192 feature = "std",
193 error("String too long: {0} bytes exceeds maximum of 65535")
194 )]
195 StringTooLong(usize),
196
197 #[cfg_attr(feature = "std", error("Configuration error: {0}"))]
198 Configuration(String),
199}
200
201#[cfg(not(feature = "std"))]
202impl fmt::Display for MqttError {
203 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204 match self {
205 Self::Io(s) => write!(f, "IO error: {s}"),
206 Self::InvalidTopicName(s) => write!(f, "Invalid topic name: {s}"),
207 Self::InvalidTopicFilter(s) => write!(f, "Invalid topic filter: {s}"),
208 Self::InvalidClientId(s) => write!(f, "Invalid client ID: {s}"),
209 Self::ConnectionError(s) => write!(f, "Connection error: {s}"),
210 Self::ConnectionRefused(r) => write!(f, "Connection refused: {r:?}"),
211 Self::ProtocolError(s) => write!(f, "Protocol error: {s}"),
212 Self::MalformedPacket(s) => write!(f, "Malformed packet: {s}"),
213 Self::PacketTooLarge { size, max } => {
214 write!(f, "Packet too large: size {size} exceeds maximum {max}")
215 }
216 Self::AuthenticationFailed => write!(f, "Authentication failed"),
217 Self::NotAuthorized => write!(f, "Not authorized"),
218 Self::NotConnected => write!(f, "Not connected"),
219 Self::AlreadyConnected => write!(f, "Already connected"),
220 Self::Timeout => write!(f, "Timeout"),
221 Self::SubscriptionFailed(r) => write!(f, "Subscription failed: {r:?}"),
222 Self::SubscriptionDenied(r) => write!(f, "Subscription denied: {r:?}"),
223 Self::UnsubscriptionFailed(r) => write!(f, "Unsubscription failed: {r:?}"),
224 Self::PublishFailed(r) => write!(f, "Publish failed: {r:?}"),
225 Self::PacketIdNotFound(id) => write!(f, "Packet identifier not found: {id}"),
226 Self::PacketIdInUse(id) => write!(f, "Packet identifier already in use: {id}"),
227 Self::InvalidQoS(q) => write!(f, "Invalid QoS: {q}"),
228 Self::InvalidPacketType(t) => write!(f, "Invalid packet type: {t}"),
229 Self::InvalidReasonCode(r) => write!(f, "Invalid reason code: {r}"),
230 Self::InvalidPropertyId(p) => write!(f, "Invalid property ID: {p}"),
231 Self::DuplicatePropertyId(p) => write!(f, "Duplicate property ID: {p}"),
232 Self::SessionExpired => write!(f, "Session expired"),
233 Self::KeepAliveTimeout => write!(f, "Keep alive timeout"),
234 Self::ServerShuttingDown => write!(f, "Server shutting down"),
235 Self::ClientClosed => write!(f, "Client closed connection"),
236 Self::ConnectionClosedByPeer => write!(f, "Connection closed by peer"),
237 Self::MaxConnectTime => write!(f, "Maximum connect time exceeded"),
238 Self::TopicAliasInvalid(a) => write!(f, "Topic alias invalid: {a}"),
239 Self::ReceiveMaximumExceeded => write!(f, "Receive maximum exceeded"),
240 Self::WillRejected => write!(f, "Will message rejected"),
241 Self::ImplementationSpecific(s) => write!(f, "Implementation specific error: {s}"),
242 Self::UnsupportedProtocolVersion => write!(f, "Unsupported protocol version"),
243 Self::InvalidState(s) => write!(f, "Invalid state: {s}"),
244 Self::ClientIdentifierNotValid => write!(f, "Client identifier not valid"),
245 Self::BadUsernameOrPassword => write!(f, "Bad username or password"),
246 Self::ServerUnavailable => write!(f, "Server unavailable"),
247 Self::ServerBusy => write!(f, "Server busy"),
248 Self::Banned => write!(f, "Banned"),
249 Self::BadAuthenticationMethod => write!(f, "Bad authentication method"),
250 Self::QuotaExceeded => write!(f, "Quota exceeded"),
251 Self::PayloadFormatInvalid => write!(f, "Payload format invalid"),
252 Self::RetainNotSupported => write!(f, "Retain not supported"),
253 Self::QoSNotSupported => write!(f, "QoS not supported"),
254 Self::UseAnotherServer => write!(f, "Use another server"),
255 Self::ServerMoved => write!(f, "Server moved"),
256 Self::SharedSubscriptionsNotSupported => {
257 write!(f, "Shared subscriptions not supported")
258 }
259 Self::ConnectionRateExceeded => write!(f, "Connection rate exceeded"),
260 Self::SubscriptionIdentifiersNotSupported => {
261 write!(f, "Subscription identifiers not supported")
262 }
263 Self::WildcardSubscriptionsNotSupported => {
264 write!(f, "Wildcard subscriptions not supported")
265 }
266 Self::MessageTooLarge => write!(f, "Message too large for queue"),
267 Self::FlowControlExceeded => write!(f, "Flow control exceeded"),
268 Self::PacketIdExhausted => write!(f, "Packet ID exhausted"),
269 Self::StringTooLong(len) => {
270 write!(f, "String too long: {len} bytes exceeds maximum of 65535")
271 }
272 Self::Configuration(s) => write!(f, "Configuration error: {s}"),
273 }
274 }
275}
276
277impl MqttError {
278 #[must_use]
279 pub fn is_normal_disconnect(&self) -> bool {
280 match self {
281 Self::ClientClosed | Self::ConnectionClosedByPeer => true,
282 Self::Io(msg)
283 if msg.contains("stream has been shut down")
284 || msg.contains("Connection reset") =>
285 {
286 true
287 }
288 _ => false,
289 }
290 }
291}
292
293#[cfg(feature = "std")]
294impl From<std::io::Error> for MqttError {
295 fn from(err: std::io::Error) -> Self {
296 MqttError::Io(err.to_string())
297 }
298}
299
300impl From<String> for MqttError {
301 fn from(msg: String) -> Self {
302 MqttError::MalformedPacket(msg)
303 }
304}
305
306impl From<&str> for MqttError {
307 fn from(msg: &str) -> Self {
308 MqttError::MalformedPacket(crate::prelude::ToString::to_string(msg))
309 }
310}
311
312#[cfg(test)]
313mod tests {
314 use super::*;
315
316 #[test]
317 fn test_error_display() {
318 let err = MqttError::InvalidTopicName("test/+/topic".to_string());
319 assert_eq!(err.to_string(), "Invalid topic name: test/+/topic");
320
321 let err = MqttError::PacketTooLarge {
322 size: 1000,
323 max: 500,
324 };
325 assert_eq!(
326 err.to_string(),
327 "Packet too large: size 1000 exceeds maximum 500"
328 );
329
330 let err = MqttError::ConnectionRefused(ReasonCode::BadUsernameOrPassword);
331 assert_eq!(err.to_string(), "Connection refused: BadUsernameOrPassword");
332 }
333
334 #[cfg(feature = "std")]
335 #[test]
336 fn test_error_from_io() {
337 use std::io;
338 let io_err = io::Error::new(io::ErrorKind::ConnectionRefused, "test");
339 let mqtt_err: MqttError = io_err.into();
340 match mqtt_err {
341 MqttError::Io(e) => assert!(e.contains("test")),
342 _ => panic!("Expected Io error"),
343 }
344 }
345
346 #[test]
347 fn test_result_type() {
348 #[allow(clippy::unnecessary_wraps)]
349 fn returns_result() -> Result<String> {
350 Ok("success".to_string())
351 }
352
353 fn returns_error() -> Result<String> {
354 Err(MqttError::NotConnected)
355 }
356
357 assert!(returns_result().is_ok());
358 assert!(returns_error().is_err());
359 }
360}