mqtt5_protocol/protocol/v5/
reason_codes.rs

1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2pub enum ReasonCode {
3    // Success codes (0x00 - 0x7F)
4    Success = 0x00, // Also used for NormalDisconnection and GrantedQoS0
5    GrantedQoS1 = 0x01,
6    GrantedQoS2 = 0x02,
7    DisconnectWithWillMessage = 0x04,
8    NoMatchingSubscribers = 0x10,
9    NoSubscriptionExisted = 0x11,
10    ContinueAuthentication = 0x18,
11    ReAuthenticate = 0x19,
12
13    // Error codes (0x80 - 0xFF)
14    UnspecifiedError = 0x80,
15    MalformedPacket = 0x81,
16    ProtocolError = 0x82,
17    ImplementationSpecificError = 0x83,
18    UnsupportedProtocolVersion = 0x84,
19    ClientIdentifierNotValid = 0x85,
20    BadUsernameOrPassword = 0x86,
21    NotAuthorized = 0x87,
22    ServerUnavailable = 0x88,
23    ServerBusy = 0x89,
24    Banned = 0x8A,
25    ServerShuttingDown = 0x8B,
26    BadAuthenticationMethod = 0x8C,
27    KeepAliveTimeout = 0x8D,
28    SessionTakenOver = 0x8E,
29    TopicFilterInvalid = 0x8F,
30    TopicNameInvalid = 0x90,
31    PacketIdentifierInUse = 0x91,
32    PacketIdentifierNotFound = 0x92,
33    ReceiveMaximumExceeded = 0x93,
34    TopicAliasInvalid = 0x94,
35    PacketTooLarge = 0x95,
36    MessageRateTooHigh = 0x96,
37    QuotaExceeded = 0x97,
38    AdministrativeAction = 0x98,
39    PayloadFormatInvalid = 0x99,
40    RetainNotSupported = 0x9A,
41    QoSNotSupported = 0x9B,
42    UseAnotherServer = 0x9C,
43    ServerMoved = 0x9D,
44    SharedSubscriptionsNotSupported = 0x9E,
45    ConnectionRateExceeded = 0x9F,
46    MaximumConnectTime = 0xA0,
47    SubscriptionIdentifiersNotSupported = 0xA1,
48    WildcardSubscriptionsNotSupported = 0xA2,
49
50    // [MQoQ§Error] MQoQ-specific error codes
51    MqoqNoFlowState = 0xB3,
52    MqoqProtocolError = 0xB4,
53    MqoqStreamTypeError = 0xB5,
54    MqoqBadFlowId = 0xB6,
55    MqoqPersistentSubError = 0xB8,
56    MqoqIncompletePacket = 0xBA,
57    MqoqFlowOpenIdle = 0xBB,
58    MqoqFlowCancelled = 0xBC,
59}
60
61// Aliases for ReasonCode::Success (0x00) used in different contexts
62pub const NORMAL_DISCONNECTION: ReasonCode = ReasonCode::Success;
63pub const GRANTED_QOS_0: ReasonCode = ReasonCode::Success;
64
65impl From<ReasonCode> for u8 {
66    fn from(code: ReasonCode) -> Self {
67        code as u8
68    }
69}
70
71impl ReasonCode {
72    #[must_use]
73    pub fn is_success(&self) -> bool {
74        u8::from(*self) < 0x80
75    }
76
77    #[must_use]
78    pub fn is_error(&self) -> bool {
79        u8::from(*self) >= 0x80
80    }
81
82    #[must_use]
83    pub fn is_mqoq_error(&self) -> bool {
84        matches!(
85            self,
86            Self::MqoqNoFlowState
87                | Self::MqoqProtocolError
88                | Self::MqoqStreamTypeError
89                | Self::MqoqBadFlowId
90                | Self::MqoqPersistentSubError
91                | Self::MqoqIncompletePacket
92                | Self::MqoqFlowOpenIdle
93                | Self::MqoqFlowCancelled
94        )
95    }
96
97    #[must_use]
98    pub fn mqoq_error_level(&self) -> Option<u8> {
99        match self {
100            Self::MqoqNoFlowState | Self::MqoqProtocolError => Some(0),
101            Self::MqoqStreamTypeError | Self::MqoqBadFlowId | Self::MqoqPersistentSubError => {
102                Some(1)
103            }
104            Self::MqoqIncompletePacket | Self::MqoqFlowOpenIdle | Self::MqoqFlowCancelled => {
105                Some(2)
106            }
107            _ => None,
108        }
109    }
110
111    #[must_use]
112    pub fn from_u8(value: u8) -> Option<Self> {
113        match value {
114            0x00 => Some(Self::Success),
115            0x01 => Some(Self::GrantedQoS1),
116            0x02 => Some(Self::GrantedQoS2),
117            0x04 => Some(Self::DisconnectWithWillMessage),
118            0x10 => Some(Self::NoMatchingSubscribers),
119            0x11 => Some(Self::NoSubscriptionExisted),
120            0x18 => Some(Self::ContinueAuthentication),
121            0x19 => Some(Self::ReAuthenticate),
122            0x80 => Some(Self::UnspecifiedError),
123            0x81 => Some(Self::MalformedPacket),
124            0x82 => Some(Self::ProtocolError),
125            0x83 => Some(Self::ImplementationSpecificError),
126            0x84 => Some(Self::UnsupportedProtocolVersion),
127            0x85 => Some(Self::ClientIdentifierNotValid),
128            0x86 => Some(Self::BadUsernameOrPassword),
129            0x87 => Some(Self::NotAuthorized),
130            0x88 => Some(Self::ServerUnavailable),
131            0x89 => Some(Self::ServerBusy),
132            0x8A => Some(Self::Banned),
133            0x8B => Some(Self::ServerShuttingDown),
134            0x8C => Some(Self::BadAuthenticationMethod),
135            0x8D => Some(Self::KeepAliveTimeout),
136            0x8E => Some(Self::SessionTakenOver),
137            0x8F => Some(Self::TopicFilterInvalid),
138            0x90 => Some(Self::TopicNameInvalid),
139            0x91 => Some(Self::PacketIdentifierInUse),
140            0x92 => Some(Self::PacketIdentifierNotFound),
141            0x93 => Some(Self::ReceiveMaximumExceeded),
142            0x94 => Some(Self::TopicAliasInvalid),
143            0x95 => Some(Self::PacketTooLarge),
144            0x96 => Some(Self::MessageRateTooHigh),
145            0x97 => Some(Self::QuotaExceeded),
146            0x98 => Some(Self::AdministrativeAction),
147            0x99 => Some(Self::PayloadFormatInvalid),
148            0x9A => Some(Self::RetainNotSupported),
149            0x9B => Some(Self::QoSNotSupported),
150            0x9C => Some(Self::UseAnotherServer),
151            0x9D => Some(Self::ServerMoved),
152            0x9E => Some(Self::SharedSubscriptionsNotSupported),
153            0x9F => Some(Self::ConnectionRateExceeded),
154            0xA0 => Some(Self::MaximumConnectTime),
155            0xA1 => Some(Self::SubscriptionIdentifiersNotSupported),
156            0xA2 => Some(Self::WildcardSubscriptionsNotSupported),
157            0xB3 => Some(Self::MqoqNoFlowState),
158            0xB4 => Some(Self::MqoqProtocolError),
159            0xB5 => Some(Self::MqoqStreamTypeError),
160            0xB6 => Some(Self::MqoqBadFlowId),
161            0xB8 => Some(Self::MqoqPersistentSubError),
162            0xBA => Some(Self::MqoqIncompletePacket),
163            0xBB => Some(Self::MqoqFlowOpenIdle),
164            0xBC => Some(Self::MqoqFlowCancelled),
165            _ => None,
166        }
167    }
168}
169
170#[cfg(test)]
171mod tests {
172    use super::*;
173
174    #[test]
175    fn test_reason_code_success_check() {
176        assert!(ReasonCode::Success.is_success());
177        assert!(ReasonCode::GrantedQoS1.is_success());
178        assert!(ReasonCode::GrantedQoS2.is_success());
179        assert!(ReasonCode::NoMatchingSubscribers.is_success());
180
181        assert!(!ReasonCode::UnspecifiedError.is_success());
182        assert!(!ReasonCode::MalformedPacket.is_success());
183        assert!(!ReasonCode::NotAuthorized.is_success());
184    }
185
186    #[test]
187    fn test_reason_code_error_check() {
188        assert!(!ReasonCode::Success.is_error());
189        assert!(!ReasonCode::GrantedQoS1.is_error());
190
191        assert!(ReasonCode::UnspecifiedError.is_error());
192        assert!(ReasonCode::MalformedPacket.is_error());
193        assert!(ReasonCode::ProtocolError.is_error());
194        assert!(ReasonCode::ServerBusy.is_error());
195    }
196
197    #[test]
198    fn test_reason_code_from_u8() {
199        assert_eq!(ReasonCode::from_u8(0x00), Some(ReasonCode::Success));
200        assert_eq!(ReasonCode::from_u8(0x01), Some(ReasonCode::GrantedQoS1));
201        assert_eq!(ReasonCode::from_u8(0x02), Some(ReasonCode::GrantedQoS2));
202        assert_eq!(
203            ReasonCode::from_u8(0x80),
204            Some(ReasonCode::UnspecifiedError)
205        );
206        assert_eq!(ReasonCode::from_u8(0x81), Some(ReasonCode::MalformedPacket));
207        assert_eq!(ReasonCode::from_u8(0x87), Some(ReasonCode::NotAuthorized));
208        assert_eq!(
209            ReasonCode::from_u8(0xA2),
210            Some(ReasonCode::WildcardSubscriptionsNotSupported)
211        );
212
213        // Invalid codes
214        assert_eq!(ReasonCode::from_u8(0x03), None);
215        assert_eq!(ReasonCode::from_u8(0x05), None);
216        assert_eq!(ReasonCode::from_u8(0xFF), None);
217    }
218
219    #[test]
220    fn test_reason_code_aliases() {
221        assert_eq!(NORMAL_DISCONNECTION, ReasonCode::Success);
222        assert_eq!(GRANTED_QOS_0, ReasonCode::Success);
223        assert_eq!(NORMAL_DISCONNECTION as u8, 0x00);
224        assert_eq!(GRANTED_QOS_0 as u8, 0x00);
225    }
226
227    #[test]
228    fn test_reason_code_values() {
229        assert_eq!(ReasonCode::Success as u8, 0x00);
230        assert_eq!(ReasonCode::DisconnectWithWillMessage as u8, 0x04);
231        assert_eq!(ReasonCode::NoMatchingSubscribers as u8, 0x10);
232        assert_eq!(ReasonCode::UnspecifiedError as u8, 0x80);
233        assert_eq!(ReasonCode::ClientIdentifierNotValid as u8, 0x85);
234        assert_eq!(ReasonCode::ServerBusy as u8, 0x89);
235        assert_eq!(ReasonCode::QuotaExceeded as u8, 0x97);
236        assert_eq!(ReasonCode::WildcardSubscriptionsNotSupported as u8, 0xA2);
237    }
238
239    #[test]
240    fn test_mqoq_error_codes() {
241        assert_eq!(ReasonCode::MqoqNoFlowState as u8, 0xB3);
242        assert_eq!(ReasonCode::MqoqProtocolError as u8, 0xB4);
243        assert_eq!(ReasonCode::MqoqStreamTypeError as u8, 0xB5);
244        assert_eq!(ReasonCode::MqoqBadFlowId as u8, 0xB6);
245        assert_eq!(ReasonCode::MqoqPersistentSubError as u8, 0xB8);
246        assert_eq!(ReasonCode::MqoqIncompletePacket as u8, 0xBA);
247        assert_eq!(ReasonCode::MqoqFlowOpenIdle as u8, 0xBB);
248        assert_eq!(ReasonCode::MqoqFlowCancelled as u8, 0xBC);
249    }
250
251    #[test]
252    fn test_mqoq_error_from_u8() {
253        assert_eq!(ReasonCode::from_u8(0xB3), Some(ReasonCode::MqoqNoFlowState));
254        assert_eq!(
255            ReasonCode::from_u8(0xB4),
256            Some(ReasonCode::MqoqProtocolError)
257        );
258        assert_eq!(
259            ReasonCode::from_u8(0xB5),
260            Some(ReasonCode::MqoqStreamTypeError)
261        );
262        assert_eq!(ReasonCode::from_u8(0xB6), Some(ReasonCode::MqoqBadFlowId));
263        assert_eq!(
264            ReasonCode::from_u8(0xB8),
265            Some(ReasonCode::MqoqPersistentSubError)
266        );
267        assert_eq!(
268            ReasonCode::from_u8(0xBA),
269            Some(ReasonCode::MqoqIncompletePacket)
270        );
271        assert_eq!(
272            ReasonCode::from_u8(0xBB),
273            Some(ReasonCode::MqoqFlowOpenIdle)
274        );
275        assert_eq!(
276            ReasonCode::from_u8(0xBC),
277            Some(ReasonCode::MqoqFlowCancelled)
278        );
279        assert_eq!(ReasonCode::from_u8(0xB7), None);
280        assert_eq!(ReasonCode::from_u8(0xB9), None);
281    }
282
283    #[test]
284    fn test_mqoq_error_detection() {
285        assert!(ReasonCode::MqoqNoFlowState.is_mqoq_error());
286        assert!(ReasonCode::MqoqProtocolError.is_mqoq_error());
287        assert!(ReasonCode::MqoqStreamTypeError.is_mqoq_error());
288        assert!(ReasonCode::MqoqBadFlowId.is_mqoq_error());
289        assert!(ReasonCode::MqoqPersistentSubError.is_mqoq_error());
290        assert!(ReasonCode::MqoqIncompletePacket.is_mqoq_error());
291        assert!(ReasonCode::MqoqFlowOpenIdle.is_mqoq_error());
292        assert!(ReasonCode::MqoqFlowCancelled.is_mqoq_error());
293        assert!(!ReasonCode::Success.is_mqoq_error());
294        assert!(!ReasonCode::ProtocolError.is_mqoq_error());
295    }
296
297    #[test]
298    fn test_mqoq_error_levels() {
299        assert_eq!(ReasonCode::MqoqNoFlowState.mqoq_error_level(), Some(0));
300        assert_eq!(ReasonCode::MqoqProtocolError.mqoq_error_level(), Some(0));
301        assert_eq!(ReasonCode::MqoqStreamTypeError.mqoq_error_level(), Some(1));
302        assert_eq!(ReasonCode::MqoqBadFlowId.mqoq_error_level(), Some(1));
303        assert_eq!(
304            ReasonCode::MqoqPersistentSubError.mqoq_error_level(),
305            Some(1)
306        );
307        assert_eq!(ReasonCode::MqoqIncompletePacket.mqoq_error_level(), Some(2));
308        assert_eq!(ReasonCode::MqoqFlowOpenIdle.mqoq_error_level(), Some(2));
309        assert_eq!(ReasonCode::MqoqFlowCancelled.mqoq_error_level(), Some(2));
310        assert_eq!(ReasonCode::Success.mqoq_error_level(), None);
311        assert_eq!(ReasonCode::ProtocolError.mqoq_error_level(), None);
312    }
313}