mqtt_protocol_core/mqtt/
result_code.rs

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