Skip to main content

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    MqoqNoFlowState = 0xB3,
51    MqoqNotFlowOwner = 0xB4,
52    MqoqStreamTypeError = 0xB5,
53    MqoqBadFlowId = 0xB6,
54    MqoqPersistentTopic = 0xB7,
55    MqoqPersistentSubError = 0xB8,
56    MqoqOptionalHeader = 0xB9,
57    MqoqIncompletePacket = 0xBA,
58    MqoqFlowOpenIdle = 0xBB,
59    MqoqFlowCancelled = 0xBC,
60    MqoqFlowPacketCancelled = 0xBD,
61    MqoqFlowRefused = 0xBE,
62    MqoqDiscardState = 0xBF,
63    MqoqServerPushNotWelcome = 0xC0,
64    MqoqRecoveryFailed = 0xC1,
65}
66
67// Aliases for ReasonCode::Success (0x00) used in different contexts
68pub const NORMAL_DISCONNECTION: ReasonCode = ReasonCode::Success;
69pub const GRANTED_QOS_0: ReasonCode = ReasonCode::Success;
70
71impl From<ReasonCode> for u8 {
72    fn from(code: ReasonCode) -> Self {
73        code as u8
74    }
75}
76
77impl ReasonCode {
78    #[must_use]
79    pub fn is_success(&self) -> bool {
80        u8::from(*self) < 0x80
81    }
82
83    #[must_use]
84    pub fn is_error(&self) -> bool {
85        u8::from(*self) >= 0x80
86    }
87
88    #[must_use]
89    pub fn is_mqoq_error(&self) -> bool {
90        matches!(
91            self,
92            Self::MqoqNoFlowState
93                | Self::MqoqNotFlowOwner
94                | Self::MqoqStreamTypeError
95                | Self::MqoqBadFlowId
96                | Self::MqoqPersistentTopic
97                | Self::MqoqPersistentSubError
98                | Self::MqoqOptionalHeader
99                | Self::MqoqIncompletePacket
100                | Self::MqoqFlowOpenIdle
101                | Self::MqoqFlowCancelled
102                | Self::MqoqFlowPacketCancelled
103                | Self::MqoqFlowRefused
104                | Self::MqoqDiscardState
105                | Self::MqoqServerPushNotWelcome
106                | Self::MqoqRecoveryFailed
107        )
108    }
109
110    #[must_use]
111    pub fn mqoq_error_level(&self) -> Option<u8> {
112        match self {
113            Self::MqoqNoFlowState | Self::MqoqNotFlowOwner => Some(0),
114            Self::MqoqStreamTypeError
115            | Self::MqoqBadFlowId
116            | Self::MqoqPersistentTopic
117            | Self::MqoqPersistentSubError
118            | Self::MqoqOptionalHeader
119            | Self::MqoqDiscardState
120            | Self::MqoqServerPushNotWelcome
121            | Self::MqoqRecoveryFailed => Some(1),
122            Self::MqoqIncompletePacket
123            | Self::MqoqFlowOpenIdle
124            | Self::MqoqFlowCancelled
125            | Self::MqoqFlowPacketCancelled
126            | Self::MqoqFlowRefused => Some(2),
127            _ => None,
128        }
129    }
130
131    #[must_use]
132    pub fn from_u8(value: u8) -> Option<Self> {
133        match value {
134            0x00 => Some(Self::Success),
135            0x01 => Some(Self::GrantedQoS1),
136            0x02 => Some(Self::GrantedQoS2),
137            0x04 => Some(Self::DisconnectWithWillMessage),
138            0x10 => Some(Self::NoMatchingSubscribers),
139            0x11 => Some(Self::NoSubscriptionExisted),
140            0x18 => Some(Self::ContinueAuthentication),
141            0x19 => Some(Self::ReAuthenticate),
142            0x80 => Some(Self::UnspecifiedError),
143            0x81 => Some(Self::MalformedPacket),
144            0x82 => Some(Self::ProtocolError),
145            0x83 => Some(Self::ImplementationSpecificError),
146            0x84 => Some(Self::UnsupportedProtocolVersion),
147            0x85 => Some(Self::ClientIdentifierNotValid),
148            0x86 => Some(Self::BadUsernameOrPassword),
149            0x87 => Some(Self::NotAuthorized),
150            0x88 => Some(Self::ServerUnavailable),
151            0x89 => Some(Self::ServerBusy),
152            0x8A => Some(Self::Banned),
153            0x8B => Some(Self::ServerShuttingDown),
154            0x8C => Some(Self::BadAuthenticationMethod),
155            0x8D => Some(Self::KeepAliveTimeout),
156            0x8E => Some(Self::SessionTakenOver),
157            0x8F => Some(Self::TopicFilterInvalid),
158            0x90 => Some(Self::TopicNameInvalid),
159            0x91 => Some(Self::PacketIdentifierInUse),
160            0x92 => Some(Self::PacketIdentifierNotFound),
161            0x93 => Some(Self::ReceiveMaximumExceeded),
162            0x94 => Some(Self::TopicAliasInvalid),
163            0x95 => Some(Self::PacketTooLarge),
164            0x96 => Some(Self::MessageRateTooHigh),
165            0x97 => Some(Self::QuotaExceeded),
166            0x98 => Some(Self::AdministrativeAction),
167            0x99 => Some(Self::PayloadFormatInvalid),
168            0x9A => Some(Self::RetainNotSupported),
169            0x9B => Some(Self::QoSNotSupported),
170            0x9C => Some(Self::UseAnotherServer),
171            0x9D => Some(Self::ServerMoved),
172            0x9E => Some(Self::SharedSubscriptionsNotSupported),
173            0x9F => Some(Self::ConnectionRateExceeded),
174            0xA0 => Some(Self::MaximumConnectTime),
175            0xA1 => Some(Self::SubscriptionIdentifiersNotSupported),
176            0xA2 => Some(Self::WildcardSubscriptionsNotSupported),
177            0xB3 => Some(Self::MqoqNoFlowState),
178            0xB4 => Some(Self::MqoqNotFlowOwner),
179            0xB5 => Some(Self::MqoqStreamTypeError),
180            0xB6 => Some(Self::MqoqBadFlowId),
181            0xB7 => Some(Self::MqoqPersistentTopic),
182            0xB8 => Some(Self::MqoqPersistentSubError),
183            0xB9 => Some(Self::MqoqOptionalHeader),
184            0xBA => Some(Self::MqoqIncompletePacket),
185            0xBB => Some(Self::MqoqFlowOpenIdle),
186            0xBC => Some(Self::MqoqFlowCancelled),
187            0xBD => Some(Self::MqoqFlowPacketCancelled),
188            0xBE => Some(Self::MqoqFlowRefused),
189            0xBF => Some(Self::MqoqDiscardState),
190            0xC0 => Some(Self::MqoqServerPushNotWelcome),
191            0xC1 => Some(Self::MqoqRecoveryFailed),
192            _ => None,
193        }
194    }
195
196    #[must_use]
197    pub fn to_quic_stream_code(self) -> Option<crate::quic_error_codes::QuicStreamCode> {
198        crate::quic_error_codes::QuicStreamCode::from_code(u32::from(self as u8))
199    }
200
201    #[must_use]
202    pub fn from_quic_stream_code(code: crate::quic_error_codes::QuicStreamCode) -> Option<Self> {
203        let value = code.code();
204        if value > 255 {
205            return None;
206        }
207        #[allow(clippy::cast_possible_truncation)]
208        Self::from_u8(value as u8)
209    }
210}
211
212#[cfg(test)]
213mod tests {
214    use super::*;
215
216    #[test]
217    fn test_reason_code_success_check() {
218        assert!(ReasonCode::Success.is_success());
219        assert!(ReasonCode::GrantedQoS1.is_success());
220        assert!(ReasonCode::GrantedQoS2.is_success());
221        assert!(ReasonCode::NoMatchingSubscribers.is_success());
222
223        assert!(!ReasonCode::UnspecifiedError.is_success());
224        assert!(!ReasonCode::MalformedPacket.is_success());
225        assert!(!ReasonCode::NotAuthorized.is_success());
226    }
227
228    #[test]
229    fn test_reason_code_error_check() {
230        assert!(!ReasonCode::Success.is_error());
231        assert!(!ReasonCode::GrantedQoS1.is_error());
232
233        assert!(ReasonCode::UnspecifiedError.is_error());
234        assert!(ReasonCode::MalformedPacket.is_error());
235        assert!(ReasonCode::ProtocolError.is_error());
236        assert!(ReasonCode::ServerBusy.is_error());
237    }
238
239    #[test]
240    fn test_reason_code_from_u8() {
241        assert_eq!(ReasonCode::from_u8(0x00), Some(ReasonCode::Success));
242        assert_eq!(ReasonCode::from_u8(0x01), Some(ReasonCode::GrantedQoS1));
243        assert_eq!(ReasonCode::from_u8(0x02), Some(ReasonCode::GrantedQoS2));
244        assert_eq!(
245            ReasonCode::from_u8(0x80),
246            Some(ReasonCode::UnspecifiedError)
247        );
248        assert_eq!(ReasonCode::from_u8(0x81), Some(ReasonCode::MalformedPacket));
249        assert_eq!(ReasonCode::from_u8(0x87), Some(ReasonCode::NotAuthorized));
250        assert_eq!(
251            ReasonCode::from_u8(0xA2),
252            Some(ReasonCode::WildcardSubscriptionsNotSupported)
253        );
254
255        // Invalid codes
256        assert_eq!(ReasonCode::from_u8(0x03), None);
257        assert_eq!(ReasonCode::from_u8(0x05), None);
258        assert_eq!(ReasonCode::from_u8(0xFF), None);
259    }
260
261    #[test]
262    fn test_reason_code_aliases() {
263        assert_eq!(NORMAL_DISCONNECTION, ReasonCode::Success);
264        assert_eq!(GRANTED_QOS_0, ReasonCode::Success);
265        assert_eq!(NORMAL_DISCONNECTION as u8, 0x00);
266        assert_eq!(GRANTED_QOS_0 as u8, 0x00);
267    }
268
269    #[test]
270    fn test_reason_code_values() {
271        assert_eq!(ReasonCode::Success as u8, 0x00);
272        assert_eq!(ReasonCode::DisconnectWithWillMessage as u8, 0x04);
273        assert_eq!(ReasonCode::NoMatchingSubscribers as u8, 0x10);
274        assert_eq!(ReasonCode::UnspecifiedError as u8, 0x80);
275        assert_eq!(ReasonCode::ClientIdentifierNotValid as u8, 0x85);
276        assert_eq!(ReasonCode::ServerBusy as u8, 0x89);
277        assert_eq!(ReasonCode::QuotaExceeded as u8, 0x97);
278        assert_eq!(ReasonCode::WildcardSubscriptionsNotSupported as u8, 0xA2);
279    }
280
281    #[test]
282    fn test_mqoq_error_codes() {
283        assert_eq!(ReasonCode::MqoqNoFlowState as u8, 0xB3);
284        assert_eq!(ReasonCode::MqoqNotFlowOwner as u8, 0xB4);
285        assert_eq!(ReasonCode::MqoqStreamTypeError as u8, 0xB5);
286        assert_eq!(ReasonCode::MqoqBadFlowId as u8, 0xB6);
287        assert_eq!(ReasonCode::MqoqPersistentTopic as u8, 0xB7);
288        assert_eq!(ReasonCode::MqoqPersistentSubError as u8, 0xB8);
289        assert_eq!(ReasonCode::MqoqOptionalHeader as u8, 0xB9);
290        assert_eq!(ReasonCode::MqoqIncompletePacket as u8, 0xBA);
291        assert_eq!(ReasonCode::MqoqFlowOpenIdle as u8, 0xBB);
292        assert_eq!(ReasonCode::MqoqFlowCancelled as u8, 0xBC);
293        assert_eq!(ReasonCode::MqoqFlowPacketCancelled as u8, 0xBD);
294        assert_eq!(ReasonCode::MqoqFlowRefused as u8, 0xBE);
295        assert_eq!(ReasonCode::MqoqDiscardState as u8, 0xBF);
296        assert_eq!(ReasonCode::MqoqServerPushNotWelcome as u8, 0xC0);
297        assert_eq!(ReasonCode::MqoqRecoveryFailed as u8, 0xC1);
298    }
299
300    #[test]
301    fn test_mqoq_error_from_u8() {
302        assert_eq!(ReasonCode::from_u8(0xB3), Some(ReasonCode::MqoqNoFlowState));
303        assert_eq!(
304            ReasonCode::from_u8(0xB4),
305            Some(ReasonCode::MqoqNotFlowOwner)
306        );
307        assert_eq!(
308            ReasonCode::from_u8(0xB5),
309            Some(ReasonCode::MqoqStreamTypeError)
310        );
311        assert_eq!(ReasonCode::from_u8(0xB6), Some(ReasonCode::MqoqBadFlowId));
312        assert_eq!(
313            ReasonCode::from_u8(0xB7),
314            Some(ReasonCode::MqoqPersistentTopic)
315        );
316        assert_eq!(
317            ReasonCode::from_u8(0xB8),
318            Some(ReasonCode::MqoqPersistentSubError)
319        );
320        assert_eq!(
321            ReasonCode::from_u8(0xB9),
322            Some(ReasonCode::MqoqOptionalHeader)
323        );
324        assert_eq!(
325            ReasonCode::from_u8(0xBA),
326            Some(ReasonCode::MqoqIncompletePacket)
327        );
328        assert_eq!(
329            ReasonCode::from_u8(0xBB),
330            Some(ReasonCode::MqoqFlowOpenIdle)
331        );
332        assert_eq!(
333            ReasonCode::from_u8(0xBC),
334            Some(ReasonCode::MqoqFlowCancelled)
335        );
336        assert_eq!(
337            ReasonCode::from_u8(0xBD),
338            Some(ReasonCode::MqoqFlowPacketCancelled)
339        );
340        assert_eq!(ReasonCode::from_u8(0xBE), Some(ReasonCode::MqoqFlowRefused));
341        assert_eq!(
342            ReasonCode::from_u8(0xBF),
343            Some(ReasonCode::MqoqDiscardState)
344        );
345        assert_eq!(
346            ReasonCode::from_u8(0xC0),
347            Some(ReasonCode::MqoqServerPushNotWelcome)
348        );
349        assert_eq!(
350            ReasonCode::from_u8(0xC1),
351            Some(ReasonCode::MqoqRecoveryFailed)
352        );
353    }
354
355    #[test]
356    fn test_mqoq_error_detection() {
357        assert!(ReasonCode::MqoqNoFlowState.is_mqoq_error());
358        assert!(ReasonCode::MqoqNotFlowOwner.is_mqoq_error());
359        assert!(ReasonCode::MqoqStreamTypeError.is_mqoq_error());
360        assert!(ReasonCode::MqoqBadFlowId.is_mqoq_error());
361        assert!(ReasonCode::MqoqPersistentTopic.is_mqoq_error());
362        assert!(ReasonCode::MqoqPersistentSubError.is_mqoq_error());
363        assert!(ReasonCode::MqoqOptionalHeader.is_mqoq_error());
364        assert!(ReasonCode::MqoqIncompletePacket.is_mqoq_error());
365        assert!(ReasonCode::MqoqFlowOpenIdle.is_mqoq_error());
366        assert!(ReasonCode::MqoqFlowCancelled.is_mqoq_error());
367        assert!(ReasonCode::MqoqFlowPacketCancelled.is_mqoq_error());
368        assert!(ReasonCode::MqoqFlowRefused.is_mqoq_error());
369        assert!(ReasonCode::MqoqDiscardState.is_mqoq_error());
370        assert!(ReasonCode::MqoqServerPushNotWelcome.is_mqoq_error());
371        assert!(ReasonCode::MqoqRecoveryFailed.is_mqoq_error());
372        assert!(!ReasonCode::Success.is_mqoq_error());
373        assert!(!ReasonCode::ProtocolError.is_mqoq_error());
374    }
375
376    #[test]
377    fn test_mqoq_error_levels() {
378        assert_eq!(ReasonCode::MqoqNoFlowState.mqoq_error_level(), Some(0));
379        assert_eq!(ReasonCode::MqoqNotFlowOwner.mqoq_error_level(), Some(0));
380        assert_eq!(ReasonCode::MqoqStreamTypeError.mqoq_error_level(), Some(1));
381        assert_eq!(ReasonCode::MqoqBadFlowId.mqoq_error_level(), Some(1));
382        assert_eq!(ReasonCode::MqoqPersistentTopic.mqoq_error_level(), Some(1));
383        assert_eq!(
384            ReasonCode::MqoqPersistentSubError.mqoq_error_level(),
385            Some(1)
386        );
387        assert_eq!(ReasonCode::MqoqOptionalHeader.mqoq_error_level(), Some(1));
388        assert_eq!(ReasonCode::MqoqDiscardState.mqoq_error_level(), Some(1));
389        assert_eq!(
390            ReasonCode::MqoqServerPushNotWelcome.mqoq_error_level(),
391            Some(1)
392        );
393        assert_eq!(ReasonCode::MqoqRecoveryFailed.mqoq_error_level(), Some(1));
394        assert_eq!(ReasonCode::MqoqIncompletePacket.mqoq_error_level(), Some(2));
395        assert_eq!(ReasonCode::MqoqFlowOpenIdle.mqoq_error_level(), Some(2));
396        assert_eq!(ReasonCode::MqoqFlowCancelled.mqoq_error_level(), Some(2));
397        assert_eq!(
398            ReasonCode::MqoqFlowPacketCancelled.mqoq_error_level(),
399            Some(2)
400        );
401        assert_eq!(ReasonCode::MqoqFlowRefused.mqoq_error_level(), Some(2));
402        assert_eq!(ReasonCode::Success.mqoq_error_level(), None);
403        assert_eq!(ReasonCode::ProtocolError.mqoq_error_level(), None);
404    }
405
406    #[test]
407    fn test_reason_to_quic_stream_code_round_trip() {
408        use crate::quic_error_codes::QuicStreamCode;
409
410        let pairs = [
411            (ReasonCode::MqoqNoFlowState, QuicStreamCode::NoFlowState),
412            (ReasonCode::MqoqNotFlowOwner, QuicStreamCode::NotFlowOwner),
413            (ReasonCode::MqoqStreamTypeError, QuicStreamCode::StreamType),
414            (ReasonCode::MqoqBadFlowId, QuicStreamCode::BadFlowId),
415            (
416                ReasonCode::MqoqPersistentTopic,
417                QuicStreamCode::PersistentTopic,
418            ),
419            (
420                ReasonCode::MqoqPersistentSubError,
421                QuicStreamCode::PersistentSub,
422            ),
423            (
424                ReasonCode::MqoqOptionalHeader,
425                QuicStreamCode::OptionalHeader,
426            ),
427            (
428                ReasonCode::MqoqIncompletePacket,
429                QuicStreamCode::IncompletePacket,
430            ),
431            (ReasonCode::MqoqFlowOpenIdle, QuicStreamCode::FlowOpenIdle),
432            (ReasonCode::MqoqFlowCancelled, QuicStreamCode::FlowCancelled),
433            (
434                ReasonCode::MqoqFlowPacketCancelled,
435                QuicStreamCode::FlowPacketCancelled,
436            ),
437            (ReasonCode::MqoqFlowRefused, QuicStreamCode::FlowRefused),
438            (ReasonCode::MqoqDiscardState, QuicStreamCode::DiscardState),
439            (
440                ReasonCode::MqoqServerPushNotWelcome,
441                QuicStreamCode::ServerPushNotWelcome,
442            ),
443            (
444                ReasonCode::MqoqRecoveryFailed,
445                QuicStreamCode::RecoveryFailed,
446            ),
447        ];
448
449        for (reason, stream) in pairs {
450            assert_eq!(reason.to_quic_stream_code(), Some(stream));
451            assert_eq!(ReasonCode::from_quic_stream_code(stream), Some(reason));
452        }
453    }
454}