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#[cfg_attr(feature = "defmt", derive(defmt::Format))]
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<core::convert::Infallible> for MqttError {
96    fn from(_: core::convert::Infallible) -> Self {
97        // Infallible is an error that never occurs, so this is never actually called
98        unreachable!()
99    }
100}
101
102impl core::fmt::Display for MqttError {
103    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::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(&alloc::format!("{self}"))
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#[cfg_attr(feature = "defmt", derive(defmt::Format))]
215#[repr(u8)]
216pub enum ConnectReturnCode {
217    Accepted = 0,                    // Connection accepted (not an error)
218    UnacceptableProtocolVersion = 1, // The Server does not support the level of the MQTT protocol requested by the Client
219    IdentifierRejected = 2, // The Client identifier is correct UTF-8 but not allowed by the Server
220    ServerUnavailable = 3, // The Network Connection has been made but the MQTT service is unavailable
221    BadUserNameOrPassword = 4, // The data in the user name or password is malformed
222    NotAuthorized = 5,     // The Client is not authorized to connect
223}
224
225impl ConnectReturnCode {
226    pub fn is_success(&self) -> bool {
227        matches!(self, Self::Accepted)
228    }
229    pub fn is_failure(&self) -> bool {
230        !self.is_success()
231    }
232}
233
234impl fmt::Display for ConnectReturnCode {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        let s = match self {
237            Self::Accepted => "Accepted",
238            Self::UnacceptableProtocolVersion => "UnacceptableProtocolVersion",
239            Self::IdentifierRejected => "IdentifierRejected",
240            Self::ServerUnavailable => "ServerUnavailable",
241            Self::BadUserNameOrPassword => "BadUserNameOrPassword",
242            Self::NotAuthorized => "NotAuthorized",
243        };
244        write!(f, "{s}")
245    }
246}
247
248impl Serialize for ConnectReturnCode {
249    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
250    where
251        S: Serializer,
252    {
253        serializer.serialize_str(&alloc::format!("{self}"))
254    }
255}
256
257/// MQTT v3.1.1 SUBACK Return Code
258#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
259#[cfg_attr(feature = "defmt", derive(defmt::Format))]
260#[repr(u8)]
261pub enum SubackReturnCode {
262    SuccessMaximumQos0 = 0x00, // Success with QoS0 (not an error)
263    SuccessMaximumQos1 = 0x01, // Success with QoS1 (not an error)
264    SuccessMaximumQos2 = 0x02, // Success with QoS2 (not an error)
265    Failure = 0x80,            // Failure
266}
267
268impl SubackReturnCode {
269    pub fn is_success(&self) -> bool {
270        matches!(
271            self,
272            Self::SuccessMaximumQos0 | Self::SuccessMaximumQos1 | Self::SuccessMaximumQos2
273        )
274    }
275    pub fn is_failure(&self) -> bool {
276        !self.is_success()
277    }
278}
279
280impl fmt::Display for SubackReturnCode {
281    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
282        let s = match self {
283            Self::SuccessMaximumQos0 => "SuccessMaximumQos0",
284            Self::SuccessMaximumQos1 => "SuccessMaximumQos1",
285            Self::SuccessMaximumQos2 => "SuccessMaximumQos2",
286            Self::Failure => "Failure",
287        };
288        write!(f, "{s}")
289    }
290}
291
292impl Serialize for SubackReturnCode {
293    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
294    where
295        S: Serializer,
296    {
297        serializer.serialize_str(&alloc::format!("{self}"))
298    }
299}
300
301/// MQTT v5.0 Connect Reason Code (used in CONNACK)
302#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
303#[cfg_attr(feature = "defmt", derive(defmt::Format))]
304#[repr(u8)]
305pub enum ConnectReasonCode {
306    Success = 0x00,                     // Success (not an error)
307    UnspecifiedError = 0x80,            // Unspecified error
308    MalformedPacket = 0x81,             // Malformed Packet
309    ProtocolError = 0x82,               // Protocol Error
310    ImplementationSpecificError = 0x83, // Implementation specific error
311    UnsupportedProtocolVersion = 0x84,  // Unsupported Protocol Version
312    ClientIdentifierNotValid = 0x85,    // Client Identifier not valid
313    BadUserNameOrPassword = 0x86,       // Bad User Name or Password
314    NotAuthorized = 0x87,               // Not authorized
315    ServerUnavailable = 0x88,           // Server unavailable
316    ServerBusy = 0x89,                  // Server busy
317    Banned = 0x8a,                      // Banned
318    BadAuthenticationMethod = 0x8c,     // Bad authentication method
319    TopicNameInvalid = 0x90,            // Topic Name invalid
320    PacketTooLarge = 0x95,              // Packet too large
321    QuotaExceeded = 0x97,               // Quota exceeded
322    PayloadFormatInvalid = 0x99,        // Payload format invalid
323    RetainNotSupported = 0x9a,          // Retain not supported
324    QosNotSupported = 0x9b,             // QoS not supported
325    UseAnotherServer = 0x9c,            // Use another server
326    ServerMoved = 0x9d,                 // Server moved
327    ConnectionRateExceeded = 0x9f,      // Connection rate exceeded
328}
329
330impl fmt::Display for ConnectReasonCode {
331    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332        let s = match self {
333            Self::Success => "Success",
334            Self::UnspecifiedError => "UnspecifiedError",
335            Self::MalformedPacket => "MalformedPacket",
336            Self::ProtocolError => "ProtocolError",
337            Self::ImplementationSpecificError => "ImplementationSpecificError",
338            Self::UnsupportedProtocolVersion => "UnsupportedProtocolVersion",
339            Self::ClientIdentifierNotValid => "ClientIdentifierNotValid",
340            Self::BadUserNameOrPassword => "BadUserNameOrPassword",
341            Self::NotAuthorized => "NotAuthorized",
342            Self::ServerUnavailable => "ServerUnavailable",
343            Self::ServerBusy => "ServerBusy",
344            Self::Banned => "Banned",
345            Self::BadAuthenticationMethod => "BadAuthenticationMethod",
346            Self::TopicNameInvalid => "TopicNameInvalid",
347            Self::PacketTooLarge => "PacketTooLarge",
348            Self::QuotaExceeded => "QuotaExceeded",
349            Self::PayloadFormatInvalid => "PayloadFormatInvalid",
350            Self::RetainNotSupported => "RetainNotSupported",
351            Self::QosNotSupported => "QosNotSupported",
352            Self::UseAnotherServer => "UseAnotherServer",
353            Self::ServerMoved => "ServerMoved",
354            Self::ConnectionRateExceeded => "ConnectionRateExceeded",
355        };
356        write!(f, "{s}")
357    }
358}
359
360impl Serialize for ConnectReasonCode {
361    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
362    where
363        S: Serializer,
364    {
365        serializer.serialize_str(&alloc::format!("{self}"))
366    }
367}
368
369impl From<ConnectReasonCode> for MqttError {
370    fn from(code: ConnectReasonCode) -> Self {
371        // as u8 -> TryFrom<u8> -> unwrap_or fallback
372        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
373    }
374}
375
376#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
377#[cfg_attr(feature = "defmt", derive(defmt::Format))]
378#[repr(u8)]
379pub enum DisconnectReasonCode {
380    NormalDisconnection = 0x00,
381    DisconnectWithWillMessage = 0x04,
382    UnspecifiedError = 0x80,
383    MalformedPacket = 0x81,
384    ProtocolError = 0x82,
385    ImplementationSpecificError = 0x83,
386    NotAuthorized = 0x87,
387    ServerBusy = 0x89,
388    ServerShuttingDown = 0x8b,
389    KeepAliveTimeout = 0x8d,
390    SessionTakenOver = 0x8e,
391    TopicFilterInvalid = 0x8f,
392    TopicNameInvalid = 0x90,
393    ReceiveMaximumExceeded = 0x93,
394    TopicAliasInvalid = 0x94,
395    PacketTooLarge = 0x95,
396    MessageRateTooHigh = 0x96,
397    QuotaExceeded = 0x97,
398    AdministrativeAction = 0x98,
399    PayloadFormatInvalid = 0x99,
400    RetainNotSupported = 0x9a,
401    QosNotSupported = 0x9b,
402    UseAnotherServer = 0x9c,
403    ServerMoved = 0x9d,
404    SharedSubscriptionsNotSupported = 0x9e,
405    ConnectionRateExceeded = 0x9f,
406    MaximumConnectTime = 0xa0,
407    SubscriptionIdentifiersNotSupported = 0xa1,
408    WildcardSubscriptionsNotSupported = 0xa2,
409}
410
411impl core::fmt::Display for DisconnectReasonCode {
412    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
413        let s = match self {
414            Self::NormalDisconnection => "NormalDisconnection",
415            Self::DisconnectWithWillMessage => "DisconnectWithWillMessage",
416            Self::UnspecifiedError => "UnspecifiedError",
417            Self::MalformedPacket => "MalformedPacket",
418            Self::ProtocolError => "ProtocolError",
419            Self::ImplementationSpecificError => "ImplementationSpecificError",
420            Self::NotAuthorized => "NotAuthorized",
421            Self::ServerBusy => "ServerBusy",
422            Self::ServerShuttingDown => "ServerShuttingDown",
423            Self::KeepAliveTimeout => "KeepAliveTimeout",
424            Self::SessionTakenOver => "SessionTakenOver",
425            Self::TopicFilterInvalid => "TopicFilterInvalid",
426            Self::TopicNameInvalid => "TopicNameInvalid",
427            Self::ReceiveMaximumExceeded => "ReceiveMaximumExceeded",
428            Self::TopicAliasInvalid => "TopicAliasInvalid",
429            Self::PacketTooLarge => "PacketTooLarge",
430            Self::MessageRateTooHigh => "MessageRateTooHigh",
431            Self::QuotaExceeded => "QuotaExceeded",
432            Self::AdministrativeAction => "AdministrativeAction",
433            Self::PayloadFormatInvalid => "PayloadFormatInvalid",
434            Self::RetainNotSupported => "RetainNotSupported",
435            Self::QosNotSupported => "QosNotSupported",
436            Self::UseAnotherServer => "UseAnotherServer",
437            Self::ServerMoved => "ServerMoved",
438            Self::SharedSubscriptionsNotSupported => "SharedSubscriptionsNotSupported",
439            Self::ConnectionRateExceeded => "ConnectionRateExceeded",
440            Self::MaximumConnectTime => "MaximumConnectTime",
441            Self::SubscriptionIdentifiersNotSupported => "SubscriptionIdentifiersNotSupported",
442            Self::WildcardSubscriptionsNotSupported => "WildcardSubscriptionsNotSupported",
443        };
444        write!(f, "{s}")
445    }
446}
447
448impl Serialize for DisconnectReasonCode {
449    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
450    where
451        S: Serializer,
452    {
453        serializer.serialize_str(&alloc::format!("{self}"))
454    }
455}
456
457impl From<DisconnectReasonCode> for MqttError {
458    fn from(code: DisconnectReasonCode) -> Self {
459        // as u8 -> TryFrom<u8> -> unwrap_or fallback
460        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
461    }
462}
463
464impl From<MqttError> for DisconnectReasonCode {
465    fn from(error: MqttError) -> Self {
466        match error {
467            MqttError::UnspecifiedError => DisconnectReasonCode::UnspecifiedError,
468            MqttError::MalformedPacket => DisconnectReasonCode::MalformedPacket,
469            MqttError::ProtocolError => DisconnectReasonCode::ProtocolError,
470            MqttError::ImplementationSpecificError => {
471                DisconnectReasonCode::ImplementationSpecificError
472            }
473            MqttError::NotAuthorized => DisconnectReasonCode::NotAuthorized,
474            MqttError::ServerBusy => DisconnectReasonCode::ServerBusy,
475            MqttError::ServerShuttingDown => DisconnectReasonCode::ServerShuttingDown,
476            MqttError::KeepAliveTimeout => DisconnectReasonCode::KeepAliveTimeout,
477            MqttError::SessionTakenOver => DisconnectReasonCode::SessionTakenOver,
478            MqttError::TopicFilterInvalid => DisconnectReasonCode::TopicFilterInvalid,
479            MqttError::TopicNameInvalid => DisconnectReasonCode::TopicNameInvalid,
480            MqttError::ReceiveMaximumExceeded => DisconnectReasonCode::ReceiveMaximumExceeded,
481            MqttError::TopicAliasInvalid => DisconnectReasonCode::TopicAliasInvalid,
482            MqttError::PacketTooLarge => DisconnectReasonCode::PacketTooLarge,
483            MqttError::MessageRateTooHigh => DisconnectReasonCode::MessageRateTooHigh,
484            MqttError::QuotaExceeded => DisconnectReasonCode::QuotaExceeded,
485            MqttError::AdministrativeAction => DisconnectReasonCode::AdministrativeAction,
486            MqttError::PayloadFormatInvalid => DisconnectReasonCode::PayloadFormatInvalid,
487            MqttError::RetainNotSupported => DisconnectReasonCode::RetainNotSupported,
488            MqttError::QosNotSupported => DisconnectReasonCode::QosNotSupported,
489            MqttError::UseAnotherServer => DisconnectReasonCode::UseAnotherServer,
490            MqttError::ServerMoved => DisconnectReasonCode::ServerMoved,
491            MqttError::SharedSubscriptionsNotSupported => {
492                DisconnectReasonCode::SharedSubscriptionsNotSupported
493            }
494            MqttError::ConnectionRateExceeded => DisconnectReasonCode::ConnectionRateExceeded,
495            MqttError::MaximumConnectTime => DisconnectReasonCode::MaximumConnectTime,
496            MqttError::SubscriptionIdentifiersNotSupported => {
497                DisconnectReasonCode::SubscriptionIdentifiersNotSupported
498            }
499            MqttError::WildcardSubscriptionsNotSupported => {
500                DisconnectReasonCode::WildcardSubscriptionsNotSupported
501            }
502            // All other MqttError variants map to UnspecifiedError
503            _ => DisconnectReasonCode::UnspecifiedError,
504        }
505    }
506}
507
508#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
509#[cfg_attr(feature = "defmt", derive(defmt::Format))]
510#[repr(u8)]
511pub enum SubackReasonCode {
512    GrantedQos0 = 0x00,
513    GrantedQos1 = 0x01,
514    GrantedQos2 = 0x02,
515    UnspecifiedError = 0x80,
516    ImplementationSpecificError = 0x83,
517    NotAuthorized = 0x87,
518    TopicFilterInvalid = 0x8f,
519    PacketIdentifierInUse = 0x91,
520    QuotaExceeded = 0x97,
521    SharedSubscriptionsNotSupported = 0x9e,
522    SubscriptionIdentifiersNotSupported = 0xa1,
523    WildcardSubscriptionsNotSupported = 0xa2,
524}
525
526impl SubackReasonCode {
527    pub fn is_success(&self) -> bool {
528        matches!(
529            self,
530            Self::GrantedQos0 | Self::GrantedQos1 | Self::GrantedQos2
531        )
532    }
533    pub fn is_failure(&self) -> bool {
534        !self.is_success()
535    }
536}
537
538impl core::fmt::Display for SubackReasonCode {
539    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
540        let s = match self {
541            Self::GrantedQos0 => "GrantedQos0",
542            Self::GrantedQos1 => "GrantedQos1",
543            Self::GrantedQos2 => "GrantedQos2",
544            Self::UnspecifiedError => "UnspecifiedError",
545            Self::ImplementationSpecificError => "ImplementationSpecificError",
546            Self::NotAuthorized => "NotAuthorized",
547            Self::TopicFilterInvalid => "TopicFilterInvalid",
548            Self::PacketIdentifierInUse => "PacketIdentifierInUse",
549            Self::QuotaExceeded => "QuotaExceeded",
550            Self::SharedSubscriptionsNotSupported => "SharedSubscriptionsNotSupported",
551            Self::SubscriptionIdentifiersNotSupported => "SubscriptionIdentifiersNotSupported",
552            Self::WildcardSubscriptionsNotSupported => "WildcardSubscriptionsNotSupported",
553        };
554        write!(f, "{s}")
555    }
556}
557
558impl Serialize for SubackReasonCode {
559    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
560    where
561        S: Serializer,
562    {
563        serializer.serialize_str(&alloc::format!("{self}"))
564    }
565}
566
567impl From<SubackReasonCode> for MqttError {
568    fn from(code: SubackReasonCode) -> Self {
569        // as u8 -> TryFrom<u8> -> unwrap_or fallback
570        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
571    }
572}
573
574#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
575#[cfg_attr(feature = "defmt", derive(defmt::Format))]
576#[repr(u8)]
577pub enum UnsubackReasonCode {
578    Success = 0x00,
579    NoSubscriptionExisted = 0x11,
580    UnspecifiedError = 0x80,
581    ImplementationSpecificError = 0x83,
582    NotAuthorized = 0x87,
583    TopicFilterInvalid = 0x8f,
584    PacketIdentifierInUse = 0x91,
585}
586
587impl UnsubackReasonCode {
588    pub fn is_success(&self) -> bool {
589        matches!(self, Self::Success | Self::NoSubscriptionExisted)
590    }
591    pub fn is_failure(&self) -> bool {
592        !self.is_success()
593    }
594}
595
596impl core::fmt::Display for UnsubackReasonCode {
597    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
598        let s = match self {
599            Self::Success => "Success",
600            Self::NoSubscriptionExisted => "NoSubscriptionExisted",
601            Self::UnspecifiedError => "UnspecifiedError",
602            Self::ImplementationSpecificError => "ImplementationSpecificError",
603            Self::NotAuthorized => "NotAuthorized",
604            Self::TopicFilterInvalid => "TopicFilterInvalid",
605            Self::PacketIdentifierInUse => "PacketIdentifierInUse",
606        };
607        write!(f, "{s}")
608    }
609}
610
611impl Serialize for UnsubackReasonCode {
612    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
613    where
614        S: Serializer,
615    {
616        serializer.serialize_str(&alloc::format!("{self}"))
617    }
618}
619
620impl From<UnsubackReasonCode> for MqttError {
621    fn from(code: UnsubackReasonCode) -> Self {
622        // as u8 -> TryFrom<u8> -> unwrap_or fallback
623        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
624    }
625}
626
627#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
628#[cfg_attr(feature = "defmt", derive(defmt::Format))]
629#[repr(u8)]
630pub enum PubackReasonCode {
631    Success = 0x00,
632    NoMatchingSubscribers = 0x10,
633    UnspecifiedError = 0x80,
634    ImplementationSpecificError = 0x83,
635    NotAuthorized = 0x87,
636    TopicNameInvalid = 0x90,
637    PacketIdentifierInUse = 0x91,
638    QuotaExceeded = 0x97,
639    PayloadFormatInvalid = 0x99,
640}
641
642impl PubackReasonCode {
643    pub fn is_success(&self) -> bool {
644        matches!(self, Self::Success | Self::NoMatchingSubscribers)
645    }
646    pub fn is_failure(&self) -> bool {
647        !self.is_success()
648    }
649}
650
651impl core::fmt::Display for PubackReasonCode {
652    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
653        let s = match self {
654            Self::Success => "Success",
655            Self::NoMatchingSubscribers => "NoMatchingSubscribers",
656            Self::UnspecifiedError => "UnspecifiedError",
657            Self::ImplementationSpecificError => "ImplementationSpecificError",
658            Self::NotAuthorized => "NotAuthorized",
659            Self::TopicNameInvalid => "TopicNameInvalid",
660            Self::PacketIdentifierInUse => "PacketIdentifierInUse",
661            Self::QuotaExceeded => "QuotaExceeded",
662            Self::PayloadFormatInvalid => "PayloadFormatInvalid",
663        };
664        write!(f, "{s}")
665    }
666}
667
668impl Serialize for PubackReasonCode {
669    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
670    where
671        S: Serializer,
672    {
673        serializer.serialize_str(&alloc::format!("{self}"))
674    }
675}
676
677impl From<PubackReasonCode> for MqttError {
678    fn from(code: PubackReasonCode) -> Self {
679        // as u8 -> TryFrom<u8> -> unwrap_or fallback
680        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
681    }
682}
683
684#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
685#[cfg_attr(feature = "defmt", derive(defmt::Format))]
686#[repr(u8)]
687pub enum PubrecReasonCode {
688    Success = 0x00,
689    NoMatchingSubscribers = 0x10,
690    UnspecifiedError = 0x80,
691    ImplementationSpecificError = 0x83,
692    NotAuthorized = 0x87,
693    TopicNameInvalid = 0x90,
694    PacketIdentifierInUse = 0x91,
695    QuotaExceeded = 0x97,
696    PayloadFormatInvalid = 0x99,
697}
698
699impl PubrecReasonCode {
700    pub fn is_success(&self) -> bool {
701        matches!(self, Self::Success | Self::NoMatchingSubscribers)
702    }
703    pub fn is_failure(&self) -> bool {
704        !self.is_success()
705    }
706}
707impl core::fmt::Display for PubrecReasonCode {
708    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
709        let s = match self {
710            Self::Success => "Success",
711            Self::NoMatchingSubscribers => "NoMatchingSubscribers",
712            Self::UnspecifiedError => "UnspecifiedError",
713            Self::ImplementationSpecificError => "ImplementationSpecificError",
714            Self::NotAuthorized => "NotAuthorized",
715            Self::TopicNameInvalid => "TopicNameInvalid",
716            Self::PacketIdentifierInUse => "PacketIdentifierInUse",
717            Self::QuotaExceeded => "QuotaExceeded",
718            Self::PayloadFormatInvalid => "PayloadFormatInvalid",
719        };
720        write!(f, "{s}")
721    }
722}
723
724impl Serialize for PubrecReasonCode {
725    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
726    where
727        S: Serializer,
728    {
729        serializer.serialize_str(&alloc::format!("{self}"))
730    }
731}
732
733impl From<PubrecReasonCode> for MqttError {
734    fn from(code: PubrecReasonCode) -> Self {
735        // as u8 -> TryFrom<u8> -> unwrap_or fallback
736        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
737    }
738}
739
740#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
741#[cfg_attr(feature = "defmt", derive(defmt::Format))]
742#[repr(u8)]
743pub enum PubrelReasonCode {
744    Success = 0x00,
745    PacketIdentifierNotFound = 0x92,
746}
747
748impl PubrelReasonCode {
749    pub fn is_success(&self) -> bool {
750        matches!(self, Self::Success)
751    }
752    pub fn is_failure(&self) -> bool {
753        !self.is_success()
754    }
755}
756
757impl core::fmt::Display for PubrelReasonCode {
758    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
759        let s = match self {
760            Self::Success => "Success",
761            Self::PacketIdentifierNotFound => "PacketIdentifierNotFound",
762        };
763        write!(f, "{s}")
764    }
765}
766
767impl Serialize for PubrelReasonCode {
768    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
769    where
770        S: Serializer,
771    {
772        serializer.serialize_str(&alloc::format!("{self}"))
773    }
774}
775
776impl From<PubrelReasonCode> for MqttError {
777    fn from(code: PubrelReasonCode) -> Self {
778        // as u8 -> TryFrom<u8> -> unwrap_or fallback
779        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
780    }
781}
782
783#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
784#[cfg_attr(feature = "defmt", derive(defmt::Format))]
785#[repr(u8)]
786pub enum PubcompReasonCode {
787    Success = 0x00,
788    PacketIdentifierNotFound = 0x92,
789}
790
791impl PubcompReasonCode {
792    pub fn is_success(&self) -> bool {
793        matches!(self, Self::Success)
794    }
795    pub fn is_failure(&self) -> bool {
796        !self.is_success()
797    }
798}
799
800impl core::fmt::Display for PubcompReasonCode {
801    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
802        let s = match self {
803            Self::Success => "Success",
804            Self::PacketIdentifierNotFound => "PacketIdentifierNotFound",
805        };
806        write!(f, "{s}")
807    }
808}
809
810impl Serialize for PubcompReasonCode {
811    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
812    where
813        S: Serializer,
814    {
815        serializer.serialize_str(&alloc::format!("{self}"))
816    }
817}
818
819impl From<PubcompReasonCode> for MqttError {
820    fn from(code: PubcompReasonCode) -> Self {
821        // as u8 -> TryFrom<u8> -> unwrap_or fallback
822        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
823    }
824}
825
826#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
827#[cfg_attr(feature = "defmt", derive(defmt::Format))]
828#[repr(u8)]
829pub enum AuthReasonCode {
830    Success = 0x00,
831    ContinueAuthentication = 0x18,
832    ReAuthenticate = 0x19,
833}
834
835impl AuthReasonCode {
836    pub fn is_success(&self) -> bool {
837        matches!(
838            self,
839            Self::Success | Self::ContinueAuthentication | Self::ReAuthenticate
840        )
841    }
842    pub fn is_failure(&self) -> bool {
843        !self.is_success()
844    }
845}
846
847impl core::fmt::Display for AuthReasonCode {
848    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
849        let s = match self {
850            Self::Success => "Success",
851            Self::ContinueAuthentication => "ContinueAuthentication",
852            Self::ReAuthenticate => "ReAuthenticate",
853        };
854        write!(f, "{s}")
855    }
856}
857
858impl Serialize for AuthReasonCode {
859    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
860    where
861        S: Serializer,
862    {
863        serializer.serialize_str(&alloc::format!("{self}"))
864    }
865}
866
867impl From<AuthReasonCode> for MqttError {
868    fn from(code: AuthReasonCode) -> Self {
869        // as u8 -> TryFrom<u8> -> unwrap_or fallback
870        MqttError::try_from(code as u8).unwrap_or(MqttError::ProtocolError)
871    }
872}