mqtt_protocol_core/mqtt/
result_code.rs

1/**
2 * MIT License
3 *
4 * Copyright (c) 2025 Takatoshi Kondo
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24use derive_builder::UninitializedFieldError;
25use num_enum::TryFromPrimitive;
26use serde::{Serialize, Serializer};
27use std::fmt;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30#[repr(u16)]
31pub enum MqttError {
32    // MQTT protocol based error
33    UnspecifiedError = 0x0080,
34    MalformedPacket = 0x0081,
35    ProtocolError = 0x0082,
36    ImplementationSpecificError = 0x0083,
37    UnsupportedProtocolVersion = 0x0084,
38    ClientIdentifierNotValid = 0x0085,
39    BadUserNameOrPassword = 0x0086,
40    NotAuthorized = 0x0087,
41    ServerUnavailable = 0x0088,
42    ServerBusy = 0x0089,
43    Banned = 0x008A,
44    ServerShuttingDown = 0x008B,
45    BadAuthenticationMethod = 0x008C,
46    KeepAliveTimeout = 0x008D,
47    SessionTakenOver = 0x008E,
48    TopicFilterInvalid = 0x008F,
49    TopicNameInvalid = 0x0090,
50    ReceiveMaximumExceeded = 0x0093,
51    TopicAliasInvalid = 0x0094,
52    PacketTooLarge = 0x0095,
53    MessageRateTooHigh = 0x0096,
54    QuotaExceeded = 0x0097,
55    AdministrativeAction = 0x0098,
56    PayloadFormatInvalid = 0x0099,
57    RetainNotSupported = 0x009A,
58    QosNotSupported = 0x009B,
59    UseAnotherServer = 0x009C,
60    ServerMoved = 0x009D,
61    SharedSubscriptionsNotSupported = 0x009E,
62    ConnectionRateExceeded = 0x009F,
63    MaximumConnectTime = 0x00A0,
64    SubscriptionIdentifiersNotSupported = 0x00A1,
65    WildcardSubscriptionsNotSupported = 0x00A2,
66
67    // Library error
68    PartialErrorDetected = 0x0101,
69    PacketEnqueued = 0x0102,
70    AllErrorDetected = 0x0180,
71    PacketIdentifierFullyUsed = 0x0181,
72    PacketIdentifierConflict = 0x0182,
73    PacketIdentifierInvalid = 0x0183,
74    PacketNotAllowedToSend = 0x0184,
75    PacketNotAllowedToStore = 0x0185,
76    PacketNotRegulated = 0x0186,
77    InsufficientBytes = 0x0187,
78    InvalidPacketForRole = 0x0188,
79    VersionMismatch = 0x0189,
80    PacketConversionFailed = 0x018A,
81    PacketProcessFailed = 0x018B,
82    ValueOutOfRange = 0x018C,
83    InvalidQos = 0x018D,
84}
85
86// Implement mapping from UninitializedFieldError to MqttError
87impl From<UninitializedFieldError> for MqttError {
88    fn from(_: UninitializedFieldError) -> Self {
89        // Map all uninitialized field errors to MalformedPacket
90        MqttError::MalformedPacket
91    }
92}
93
94// Implement mapping from Infallible to MqttError
95impl From<std::convert::Infallible> for MqttError {
96    fn from(_: std::convert::Infallible) -> Self {
97        // Infallible is an error that never occurs, so this is never actually called
98        unreachable!()
99    }
100}
101
102impl std::fmt::Display for MqttError {
103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        let s = match self {
105            Self::UnspecifiedError => "UnspecifiedError",
106            Self::MalformedPacket => "MalformedPacket",
107            Self::ProtocolError => "ProtocolError",
108            Self::ImplementationSpecificError => "ImplementationSpecificError",
109            Self::UnsupportedProtocolVersion => "UnsupportedProtocolVersion",
110            Self::ClientIdentifierNotValid => "ClientIdentifierNotValid",
111            Self::BadUserNameOrPassword => "BadUserNameOrPassword",
112            Self::NotAuthorized => "NotAuthorized",
113            Self::ServerUnavailable => "ServerUnavailable",
114            Self::ServerBusy => "ServerBusy",
115            Self::Banned => "Banned",
116            Self::ServerShuttingDown => "ServerShuttingDown",
117            Self::BadAuthenticationMethod => "BadAuthenticationMethod",
118            Self::KeepAliveTimeout => "KeepAliveTimeout",
119            Self::SessionTakenOver => "SessionTakenOver",
120            Self::TopicFilterInvalid => "TopicFilterInvalid",
121            Self::TopicNameInvalid => "TopicNameInvalid",
122            Self::ReceiveMaximumExceeded => "ReceiveMaximumExceeded",
123            Self::TopicAliasInvalid => "TopicAliasInvalid",
124            Self::PacketTooLarge => "PacketTooLarge",
125            Self::MessageRateTooHigh => "MessageRateTooHigh",
126            Self::QuotaExceeded => "QuotaExceeded",
127            Self::AdministrativeAction => "AdministrativeAction",
128            Self::PayloadFormatInvalid => "PayloadFormatInvalid",
129            Self::RetainNotSupported => "RetainNotSupported",
130            Self::QosNotSupported => "QosNotSupported",
131            Self::UseAnotherServer => "UseAnotherServer",
132            Self::ServerMoved => "ServerMoved",
133            Self::SharedSubscriptionsNotSupported => "SharedSubscriptionsNotSupported",
134            Self::ConnectionRateExceeded => "ConnectionRateExceeded",
135            Self::MaximumConnectTime => "MaximumConnectTime",
136            Self::SubscriptionIdentifiersNotSupported => "SubscriptionIdentifiersNotSupported",
137            Self::WildcardSubscriptionsNotSupported => "WildcardSubscriptionsNotSupported",
138
139            Self::PartialErrorDetected => "PartialErrorDetected",
140            Self::PacketEnqueued => "PacketEnqueued",
141            Self::AllErrorDetected => "AllErrorDetected",
142            Self::PacketIdentifierFullyUsed => "PacketIdentifierFullyUsed",
143            Self::PacketIdentifierConflict => "PacketIdentifierConflict",
144            Self::PacketIdentifierInvalid => "PacketIdentifierInvalid",
145            Self::PacketNotAllowedToSend => "PacketNotAllowedToSend",
146            Self::PacketNotAllowedToStore => "PacketNotAllowedToStore",
147            Self::PacketNotRegulated => "PacketNotRegulated",
148            Self::InsufficientBytes => "InsufficientBytes",
149            Self::InvalidPacketForRole => "InvalidPacketForRole",
150            Self::VersionMismatch => "VersionMismatch",
151            Self::PacketConversionFailed => "PacketConversionFailed",
152            Self::PacketProcessFailed => "PacketProcessFailed",
153            Self::ValueOutOfRange => "ValueOutOfRange",
154            Self::InvalidQos => "InvalidQos",
155        };
156        write!(f, "{s}")
157    }
158}
159
160impl Serialize for MqttError {
161    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
162    where
163        S: Serializer,
164    {
165        serializer.serialize_str(&self.to_string())
166    }
167}
168
169impl core::convert::TryFrom<u8> for MqttError {
170    type Error = u8;
171
172    fn try_from(code: u8) -> Result<Self, Self::Error> {
173        match code {
174            0x80 => Ok(Self::UnspecifiedError),
175            0x81 => Ok(Self::MalformedPacket),
176            0x82 => Ok(Self::ProtocolError),
177            0x83 => Ok(Self::ImplementationSpecificError),
178            0x84 => Ok(Self::UnsupportedProtocolVersion),
179            0x85 => Ok(Self::ClientIdentifierNotValid),
180            0x86 => Ok(Self::BadUserNameOrPassword),
181            0x87 => Ok(Self::NotAuthorized),
182            0x88 => Ok(Self::ServerUnavailable),
183            0x89 => Ok(Self::ServerBusy),
184            0x8A => Ok(Self::Banned),
185            0x8B => Ok(Self::ServerShuttingDown),
186            0x8C => Ok(Self::BadAuthenticationMethod),
187            0x8D => Ok(Self::KeepAliveTimeout),
188            0x8E => Ok(Self::SessionTakenOver),
189            0x8F => Ok(Self::TopicFilterInvalid),
190            0x90 => Ok(Self::TopicNameInvalid),
191            0x93 => Ok(Self::ReceiveMaximumExceeded),
192            0x94 => Ok(Self::TopicAliasInvalid),
193            0x95 => Ok(Self::PacketTooLarge),
194            0x96 => Ok(Self::MessageRateTooHigh),
195            0x97 => Ok(Self::QuotaExceeded),
196            0x98 => Ok(Self::AdministrativeAction),
197            0x99 => Ok(Self::PayloadFormatInvalid),
198            0x9A => Ok(Self::RetainNotSupported),
199            0x9B => Ok(Self::QosNotSupported),
200            0x9C => Ok(Self::UseAnotherServer),
201            0x9D => Ok(Self::ServerMoved),
202            0x9E => Ok(Self::SharedSubscriptionsNotSupported),
203            0x9F => Ok(Self::ConnectionRateExceeded),
204            0xA0 => Ok(Self::MaximumConnectTime),
205            0xA1 => Ok(Self::SubscriptionIdentifiersNotSupported),
206            0xA2 => Ok(Self::WildcardSubscriptionsNotSupported),
207            other => Err(other),
208        }
209    }
210}
211
212/// MQTT v3.1.1 Connect Return Code
213#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
214#[repr(u8)]
215pub enum ConnectReturnCode {
216    Accepted = 0,                    // Connection accepted (not an error)
217    UnacceptableProtocolVersion = 1, // The Server does not support the level of the MQTT protocol requested by the Client
218    IdentifierRejected = 2, // The Client identifier is correct UTF-8 but not allowed by the Server
219    ServerUnavailable = 3, // The Network Connection has been made but the MQTT service is unavailable
220    BadUserNameOrPassword = 4, // The data in the user name or password is malformed
221    NotAuthorized = 5,     // The Client is not authorized to connect
222}
223
224impl ConnectReturnCode {
225    pub fn is_success(&self) -> bool {
226        matches!(self, Self::Accepted)
227    }
228    pub fn is_failure(&self) -> bool {
229        !self.is_success()
230    }
231}
232
233impl fmt::Display for ConnectReturnCode {
234    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235        let s = match self {
236            Self::Accepted => "Accepted",
237            Self::UnacceptableProtocolVersion => "UnacceptableProtocolVersion",
238            Self::IdentifierRejected => "IdentifierRejected",
239            Self::ServerUnavailable => "ServerUnavailable",
240            Self::BadUserNameOrPassword => "BadUserNameOrPassword",
241            Self::NotAuthorized => "NotAuthorized",
242        };
243        write!(f, "{s}")
244    }
245}
246
247impl Serialize for ConnectReturnCode {
248    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
249    where
250        S: Serializer,
251    {
252        serializer.serialize_str(&self.to_string())
253    }
254}
255
256/// MQTT v3.1.1 SUBACK Return Code
257#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
258#[repr(u8)]
259pub enum SubackReturnCode {
260    SuccessMaximumQos0 = 0x00, // Success with QoS0 (not an error)
261    SuccessMaximumQos1 = 0x01, // Success with QoS1 (not an error)
262    SuccessMaximumQos2 = 0x02, // Success with QoS2 (not an error)
263    Failure = 0x80,            // Failure
264}
265
266impl SubackReturnCode {
267    pub fn is_success(&self) -> bool {
268        matches!(
269            self,
270            Self::SuccessMaximumQos0 | Self::SuccessMaximumQos1 | Self::SuccessMaximumQos2
271        )
272    }
273    pub fn is_failure(&self) -> bool {
274        !self.is_success()
275    }
276}
277
278impl fmt::Display for SubackReturnCode {
279    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
280        let s = match self {
281            Self::SuccessMaximumQos0 => "SuccessMaximumQos0",
282            Self::SuccessMaximumQos1 => "SuccessMaximumQos1",
283            Self::SuccessMaximumQos2 => "SuccessMaximumQos2",
284            Self::Failure => "Failure",
285        };
286        write!(f, "{s}")
287    }
288}
289
290impl Serialize for SubackReturnCode {
291    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
292    where
293        S: Serializer,
294    {
295        serializer.serialize_str(&self.to_string())
296    }
297}
298
299/// MQTT v5.0 Connect Reason Code (used in CONNACK)
300#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
301#[repr(u8)]
302pub enum ConnectReasonCode {
303    Success = 0x00,                     // Success (not an error)
304    UnspecifiedError = 0x80,            // Unspecified error
305    MalformedPacket = 0x81,             // Malformed Packet
306    ProtocolError = 0x82,               // Protocol Error
307    ImplementationSpecificError = 0x83, // Implementation specific error
308    UnsupportedProtocolVersion = 0x84,  // Unsupported Protocol Version
309    ClientIdentifierNotValid = 0x85,    // Client Identifier not valid
310    BadUserNameOrPassword = 0x86,       // Bad User Name or Password
311    NotAuthorized = 0x87,               // Not authorized
312    ServerUnavailable = 0x88,           // Server unavailable
313    ServerBusy = 0x89,                  // Server busy
314    Banned = 0x8a,                      // Banned
315    BadAuthenticationMethod = 0x8c,     // Bad authentication method
316    TopicNameInvalid = 0x90,            // Topic Name invalid
317    PacketTooLarge = 0x95,              // Packet too large
318    QuotaExceeded = 0x97,               // Quota exceeded
319    PayloadFormatInvalid = 0x99,        // Payload format invalid
320    RetainNotSupported = 0x9a,          // Retain not supported
321    QosNotSupported = 0x9b,             // QoS not supported
322    UseAnotherServer = 0x9c,            // Use another server
323    ServerMoved = 0x9d,                 // Server moved
324    ConnectionRateExceeded = 0x9f,      // Connection rate exceeded
325}
326
327impl fmt::Display for ConnectReasonCode {
328    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
329        let s = match self {
330            Self::Success => "Success",
331            Self::UnspecifiedError => "UnspecifiedError",
332            Self::MalformedPacket => "MalformedPacket",
333            Self::ProtocolError => "ProtocolError",
334            Self::ImplementationSpecificError => "ImplementationSpecificError",
335            Self::UnsupportedProtocolVersion => "UnsupportedProtocolVersion",
336            Self::ClientIdentifierNotValid => "ClientIdentifierNotValid",
337            Self::BadUserNameOrPassword => "BadUserNameOrPassword",
338            Self::NotAuthorized => "NotAuthorized",
339            Self::ServerUnavailable => "ServerUnavailable",
340            Self::ServerBusy => "ServerBusy",
341            Self::Banned => "Banned",
342            Self::BadAuthenticationMethod => "BadAuthenticationMethod",
343            Self::TopicNameInvalid => "TopicNameInvalid",
344            Self::PacketTooLarge => "PacketTooLarge",
345            Self::QuotaExceeded => "QuotaExceeded",
346            Self::PayloadFormatInvalid => "PayloadFormatInvalid",
347            Self::RetainNotSupported => "RetainNotSupported",
348            Self::QosNotSupported => "QosNotSupported",
349            Self::UseAnotherServer => "UseAnotherServer",
350            Self::ServerMoved => "ServerMoved",
351            Self::ConnectionRateExceeded => "ConnectionRateExceeded",
352        };
353        write!(f, "{s}")
354    }
355}
356
357impl Serialize for ConnectReasonCode {
358    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
359    where
360        S: Serializer,
361    {
362        serializer.serialize_str(&self.to_string())
363    }
364}
365
366impl From<ConnectReasonCode> for MqttError {
367    fn from(code: ConnectReasonCode) -> Self {
368        // as u8 -> TryFrom<u8> -> unwrap_or fallback
369        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
370    }
371}
372
373#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
374#[repr(u8)]
375pub enum DisconnectReasonCode {
376    NormalDisconnection = 0x00,
377    DisconnectWithWillMessage = 0x04,
378    UnspecifiedError = 0x80,
379    MalformedPacket = 0x81,
380    ProtocolError = 0x82,
381    ImplementationSpecificError = 0x83,
382    NotAuthorized = 0x87,
383    ServerBusy = 0x89,
384    ServerShuttingDown = 0x8b,
385    KeepAliveTimeout = 0x8d,
386    SessionTakenOver = 0x8e,
387    TopicFilterInvalid = 0x8f,
388    TopicNameInvalid = 0x90,
389    ReceiveMaximumExceeded = 0x93,
390    TopicAliasInvalid = 0x94,
391    PacketTooLarge = 0x95,
392    MessageRateTooHigh = 0x96,
393    QuotaExceeded = 0x97,
394    AdministrativeAction = 0x98,
395    PayloadFormatInvalid = 0x99,
396    RetainNotSupported = 0x9a,
397    QosNotSupported = 0x9b,
398    UseAnotherServer = 0x9c,
399    ServerMoved = 0x9d,
400    SharedSubscriptionsNotSupported = 0x9e,
401    ConnectionRateExceeded = 0x9f,
402    MaximumConnectTime = 0xa0,
403    SubscriptionIdentifiersNotSupported = 0xa1,
404    WildcardSubscriptionsNotSupported = 0xa2,
405}
406
407impl std::fmt::Display for DisconnectReasonCode {
408    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409        let s = match self {
410            Self::NormalDisconnection => "NormalDisconnection",
411            Self::DisconnectWithWillMessage => "DisconnectWithWillMessage",
412            Self::UnspecifiedError => "UnspecifiedError",
413            Self::MalformedPacket => "MalformedPacket",
414            Self::ProtocolError => "ProtocolError",
415            Self::ImplementationSpecificError => "ImplementationSpecificError",
416            Self::NotAuthorized => "NotAuthorized",
417            Self::ServerBusy => "ServerBusy",
418            Self::ServerShuttingDown => "ServerShuttingDown",
419            Self::KeepAliveTimeout => "KeepAliveTimeout",
420            Self::SessionTakenOver => "SessionTakenOver",
421            Self::TopicFilterInvalid => "TopicFilterInvalid",
422            Self::TopicNameInvalid => "TopicNameInvalid",
423            Self::ReceiveMaximumExceeded => "ReceiveMaximumExceeded",
424            Self::TopicAliasInvalid => "TopicAliasInvalid",
425            Self::PacketTooLarge => "PacketTooLarge",
426            Self::MessageRateTooHigh => "MessageRateTooHigh",
427            Self::QuotaExceeded => "QuotaExceeded",
428            Self::AdministrativeAction => "AdministrativeAction",
429            Self::PayloadFormatInvalid => "PayloadFormatInvalid",
430            Self::RetainNotSupported => "RetainNotSupported",
431            Self::QosNotSupported => "QosNotSupported",
432            Self::UseAnotherServer => "UseAnotherServer",
433            Self::ServerMoved => "ServerMoved",
434            Self::SharedSubscriptionsNotSupported => "SharedSubscriptionsNotSupported",
435            Self::ConnectionRateExceeded => "ConnectionRateExceeded",
436            Self::MaximumConnectTime => "MaximumConnectTime",
437            Self::SubscriptionIdentifiersNotSupported => "SubscriptionIdentifiersNotSupported",
438            Self::WildcardSubscriptionsNotSupported => "WildcardSubscriptionsNotSupported",
439        };
440        write!(f, "{s}")
441    }
442}
443
444impl Serialize for DisconnectReasonCode {
445    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
446    where
447        S: Serializer,
448    {
449        serializer.serialize_str(&self.to_string())
450    }
451}
452
453impl From<DisconnectReasonCode> for MqttError {
454    fn from(code: DisconnectReasonCode) -> Self {
455        // as u8 -> TryFrom<u8> -> unwrap_or fallback
456        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
457    }
458}
459
460impl From<MqttError> for DisconnectReasonCode {
461    fn from(error: MqttError) -> Self {
462        match error {
463            MqttError::UnspecifiedError => DisconnectReasonCode::UnspecifiedError,
464            MqttError::MalformedPacket => DisconnectReasonCode::MalformedPacket,
465            MqttError::ProtocolError => DisconnectReasonCode::ProtocolError,
466            MqttError::ImplementationSpecificError => {
467                DisconnectReasonCode::ImplementationSpecificError
468            }
469            MqttError::NotAuthorized => DisconnectReasonCode::NotAuthorized,
470            MqttError::ServerBusy => DisconnectReasonCode::ServerBusy,
471            MqttError::ServerShuttingDown => DisconnectReasonCode::ServerShuttingDown,
472            MqttError::KeepAliveTimeout => DisconnectReasonCode::KeepAliveTimeout,
473            MqttError::SessionTakenOver => DisconnectReasonCode::SessionTakenOver,
474            MqttError::TopicFilterInvalid => DisconnectReasonCode::TopicFilterInvalid,
475            MqttError::TopicNameInvalid => DisconnectReasonCode::TopicNameInvalid,
476            MqttError::ReceiveMaximumExceeded => DisconnectReasonCode::ReceiveMaximumExceeded,
477            MqttError::TopicAliasInvalid => DisconnectReasonCode::TopicAliasInvalid,
478            MqttError::PacketTooLarge => DisconnectReasonCode::PacketTooLarge,
479            MqttError::MessageRateTooHigh => DisconnectReasonCode::MessageRateTooHigh,
480            MqttError::QuotaExceeded => DisconnectReasonCode::QuotaExceeded,
481            MqttError::AdministrativeAction => DisconnectReasonCode::AdministrativeAction,
482            MqttError::PayloadFormatInvalid => DisconnectReasonCode::PayloadFormatInvalid,
483            MqttError::RetainNotSupported => DisconnectReasonCode::RetainNotSupported,
484            MqttError::QosNotSupported => DisconnectReasonCode::QosNotSupported,
485            MqttError::UseAnotherServer => DisconnectReasonCode::UseAnotherServer,
486            MqttError::ServerMoved => DisconnectReasonCode::ServerMoved,
487            MqttError::SharedSubscriptionsNotSupported => {
488                DisconnectReasonCode::SharedSubscriptionsNotSupported
489            }
490            MqttError::ConnectionRateExceeded => DisconnectReasonCode::ConnectionRateExceeded,
491            MqttError::MaximumConnectTime => DisconnectReasonCode::MaximumConnectTime,
492            MqttError::SubscriptionIdentifiersNotSupported => {
493                DisconnectReasonCode::SubscriptionIdentifiersNotSupported
494            }
495            MqttError::WildcardSubscriptionsNotSupported => {
496                DisconnectReasonCode::WildcardSubscriptionsNotSupported
497            }
498            // All other MqttError variants map to UnspecifiedError
499            _ => DisconnectReasonCode::UnspecifiedError,
500        }
501    }
502}
503
504#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
505#[repr(u8)]
506pub enum SubackReasonCode {
507    GrantedQos0 = 0x00,
508    GrantedQos1 = 0x01,
509    GrantedQos2 = 0x02,
510    UnspecifiedError = 0x80,
511    ImplementationSpecificError = 0x83,
512    NotAuthorized = 0x87,
513    TopicFilterInvalid = 0x8f,
514    PacketIdentifierInUse = 0x91,
515    QuotaExceeded = 0x97,
516    SharedSubscriptionsNotSupported = 0x9e,
517    SubscriptionIdentifiersNotSupported = 0xa1,
518    WildcardSubscriptionsNotSupported = 0xa2,
519}
520
521impl SubackReasonCode {
522    pub fn is_success(&self) -> bool {
523        matches!(
524            self,
525            Self::GrantedQos0 | Self::GrantedQos1 | Self::GrantedQos2
526        )
527    }
528    pub fn is_failure(&self) -> bool {
529        !self.is_success()
530    }
531}
532
533impl std::fmt::Display for SubackReasonCode {
534    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
535        let s = match self {
536            Self::GrantedQos0 => "GrantedQos0",
537            Self::GrantedQos1 => "GrantedQos1",
538            Self::GrantedQos2 => "GrantedQos2",
539            Self::UnspecifiedError => "UnspecifiedError",
540            Self::ImplementationSpecificError => "ImplementationSpecificError",
541            Self::NotAuthorized => "NotAuthorized",
542            Self::TopicFilterInvalid => "TopicFilterInvalid",
543            Self::PacketIdentifierInUse => "PacketIdentifierInUse",
544            Self::QuotaExceeded => "QuotaExceeded",
545            Self::SharedSubscriptionsNotSupported => "SharedSubscriptionsNotSupported",
546            Self::SubscriptionIdentifiersNotSupported => "SubscriptionIdentifiersNotSupported",
547            Self::WildcardSubscriptionsNotSupported => "WildcardSubscriptionsNotSupported",
548        };
549        write!(f, "{s}")
550    }
551}
552
553impl Serialize for SubackReasonCode {
554    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
555    where
556        S: Serializer,
557    {
558        serializer.serialize_str(&self.to_string())
559    }
560}
561
562impl From<SubackReasonCode> for MqttError {
563    fn from(code: SubackReasonCode) -> Self {
564        // as u8 -> TryFrom<u8> -> unwrap_or fallback
565        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
566    }
567}
568
569#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
570#[repr(u8)]
571pub enum UnsubackReasonCode {
572    Success = 0x00,
573    NoSubscriptionExisted = 0x11,
574    UnspecifiedError = 0x80,
575    ImplementationSpecificError = 0x83,
576    NotAuthorized = 0x87,
577    TopicFilterInvalid = 0x8f,
578    PacketIdentifierInUse = 0x91,
579}
580
581impl UnsubackReasonCode {
582    pub fn is_success(&self) -> bool {
583        matches!(self, Self::Success | Self::NoSubscriptionExisted)
584    }
585    pub fn is_failure(&self) -> bool {
586        !self.is_success()
587    }
588}
589
590impl std::fmt::Display for UnsubackReasonCode {
591    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
592        let s = match self {
593            Self::Success => "Success",
594            Self::NoSubscriptionExisted => "NoSubscriptionExisted",
595            Self::UnspecifiedError => "UnspecifiedError",
596            Self::ImplementationSpecificError => "ImplementationSpecificError",
597            Self::NotAuthorized => "NotAuthorized",
598            Self::TopicFilterInvalid => "TopicFilterInvalid",
599            Self::PacketIdentifierInUse => "PacketIdentifierInUse",
600        };
601        write!(f, "{s}")
602    }
603}
604
605impl Serialize for UnsubackReasonCode {
606    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
607    where
608        S: Serializer,
609    {
610        serializer.serialize_str(&self.to_string())
611    }
612}
613
614impl From<UnsubackReasonCode> for MqttError {
615    fn from(code: UnsubackReasonCode) -> Self {
616        // as u8 -> TryFrom<u8> -> unwrap_or fallback
617        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
618    }
619}
620
621#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
622#[repr(u8)]
623pub enum PubackReasonCode {
624    Success = 0x00,
625    NoMatchingSubscribers = 0x10,
626    UnspecifiedError = 0x80,
627    ImplementationSpecificError = 0x83,
628    NotAuthorized = 0x87,
629    TopicNameInvalid = 0x90,
630    PacketIdentifierInUse = 0x91,
631    QuotaExceeded = 0x97,
632    PayloadFormatInvalid = 0x99,
633}
634
635impl PubackReasonCode {
636    pub fn is_success(&self) -> bool {
637        matches!(self, Self::Success | Self::NoMatchingSubscribers)
638    }
639    pub fn is_failure(&self) -> bool {
640        !self.is_success()
641    }
642}
643
644impl std::fmt::Display for PubackReasonCode {
645    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
646        let s = match self {
647            Self::Success => "Success",
648            Self::NoMatchingSubscribers => "NoMatchingSubscribers",
649            Self::UnspecifiedError => "UnspecifiedError",
650            Self::ImplementationSpecificError => "ImplementationSpecificError",
651            Self::NotAuthorized => "NotAuthorized",
652            Self::TopicNameInvalid => "TopicNameInvalid",
653            Self::PacketIdentifierInUse => "PacketIdentifierInUse",
654            Self::QuotaExceeded => "QuotaExceeded",
655            Self::PayloadFormatInvalid => "PayloadFormatInvalid",
656        };
657        write!(f, "{s}")
658    }
659}
660
661impl Serialize for PubackReasonCode {
662    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
663    where
664        S: Serializer,
665    {
666        serializer.serialize_str(&self.to_string())
667    }
668}
669
670impl From<PubackReasonCode> for MqttError {
671    fn from(code: PubackReasonCode) -> Self {
672        // as u8 -> TryFrom<u8> -> unwrap_or fallback
673        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
674    }
675}
676
677#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
678#[repr(u8)]
679pub enum PubrecReasonCode {
680    Success = 0x00,
681    NoMatchingSubscribers = 0x10,
682    UnspecifiedError = 0x80,
683    ImplementationSpecificError = 0x83,
684    NotAuthorized = 0x87,
685    TopicNameInvalid = 0x90,
686    PacketIdentifierInUse = 0x91,
687    QuotaExceeded = 0x97,
688    PayloadFormatInvalid = 0x99,
689}
690
691impl PubrecReasonCode {
692    pub fn is_success(&self) -> bool {
693        matches!(self, Self::Success | Self::NoMatchingSubscribers)
694    }
695    pub fn is_failure(&self) -> bool {
696        !self.is_success()
697    }
698}
699impl std::fmt::Display for PubrecReasonCode {
700    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
701        let s = match self {
702            Self::Success => "Success",
703            Self::NoMatchingSubscribers => "NoMatchingSubscribers",
704            Self::UnspecifiedError => "UnspecifiedError",
705            Self::ImplementationSpecificError => "ImplementationSpecificError",
706            Self::NotAuthorized => "NotAuthorized",
707            Self::TopicNameInvalid => "TopicNameInvalid",
708            Self::PacketIdentifierInUse => "PacketIdentifierInUse",
709            Self::QuotaExceeded => "QuotaExceeded",
710            Self::PayloadFormatInvalid => "PayloadFormatInvalid",
711        };
712        write!(f, "{s}")
713    }
714}
715
716impl Serialize for PubrecReasonCode {
717    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
718    where
719        S: Serializer,
720    {
721        serializer.serialize_str(&self.to_string())
722    }
723}
724
725impl From<PubrecReasonCode> for MqttError {
726    fn from(code: PubrecReasonCode) -> Self {
727        // as u8 -> TryFrom<u8> -> unwrap_or fallback
728        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
729    }
730}
731
732#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
733#[repr(u8)]
734pub enum PubrelReasonCode {
735    Success = 0x00,
736    PacketIdentifierNotFound = 0x92,
737}
738
739impl PubrelReasonCode {
740    pub fn is_success(&self) -> bool {
741        matches!(self, Self::Success)
742    }
743    pub fn is_failure(&self) -> bool {
744        !self.is_success()
745    }
746}
747
748impl std::fmt::Display for PubrelReasonCode {
749    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
750        let s = match self {
751            Self::Success => "Success",
752            Self::PacketIdentifierNotFound => "PacketIdentifierNotFound",
753        };
754        write!(f, "{s}")
755    }
756}
757
758impl Serialize for PubrelReasonCode {
759    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
760    where
761        S: Serializer,
762    {
763        serializer.serialize_str(&self.to_string())
764    }
765}
766
767impl From<PubrelReasonCode> for MqttError {
768    fn from(code: PubrelReasonCode) -> Self {
769        // as u8 -> TryFrom<u8> -> unwrap_or fallback
770        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
771    }
772}
773
774#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
775#[repr(u8)]
776pub enum PubcompReasonCode {
777    Success = 0x00,
778    PacketIdentifierNotFound = 0x92,
779}
780
781impl PubcompReasonCode {
782    pub fn is_success(&self) -> bool {
783        matches!(self, Self::Success)
784    }
785    pub fn is_failure(&self) -> bool {
786        !self.is_success()
787    }
788}
789
790impl std::fmt::Display for PubcompReasonCode {
791    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
792        let s = match self {
793            Self::Success => "Success",
794            Self::PacketIdentifierNotFound => "PacketIdentifierNotFound",
795        };
796        write!(f, "{s}")
797    }
798}
799
800impl Serialize for PubcompReasonCode {
801    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
802    where
803        S: Serializer,
804    {
805        serializer.serialize_str(&self.to_string())
806    }
807}
808
809impl From<PubcompReasonCode> for MqttError {
810    fn from(code: PubcompReasonCode) -> Self {
811        // as u8 -> TryFrom<u8> -> unwrap_or fallback
812        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
813    }
814}
815
816#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
817#[repr(u8)]
818pub enum AuthReasonCode {
819    Success = 0x00,
820    ContinueAuthentication = 0x18,
821    ReAuthenticate = 0x19,
822}
823
824impl AuthReasonCode {
825    pub fn is_success(&self) -> bool {
826        matches!(
827            self,
828            Self::Success | Self::ContinueAuthentication | Self::ReAuthenticate
829        )
830    }
831    pub fn is_failure(&self) -> bool {
832        !self.is_success()
833    }
834}
835
836impl std::fmt::Display for AuthReasonCode {
837    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
838        let s = match self {
839            Self::Success => "Success",
840            Self::ContinueAuthentication => "ContinueAuthentication",
841            Self::ReAuthenticate => "ReAuthenticate",
842        };
843        write!(f, "{s}")
844    }
845}
846
847impl Serialize for AuthReasonCode {
848    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
849    where
850        S: Serializer,
851    {
852        serializer.serialize_str(&self.to_string())
853    }
854}
855
856impl From<AuthReasonCode> for MqttError {
857    fn from(code: AuthReasonCode) -> Self {
858        // as u8 -> TryFrom<u8> -> unwrap_or fallback
859        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
860    }
861}