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
51// Aliases for ReasonCode::Success (0x00) used in different contexts
52pub const NORMAL_DISCONNECTION: ReasonCode = ReasonCode::Success;
53pub const GRANTED_QOS_0: ReasonCode = ReasonCode::Success;
54
55impl From<ReasonCode> for u8 {
56    fn from(code: ReasonCode) -> Self {
57        code as u8
58    }
59}
60
61impl ReasonCode {
62    #[must_use]
63    pub fn is_success(&self) -> bool {
64        u8::from(*self) < 0x80
65    }
66
67    #[must_use]
68    pub fn is_error(&self) -> bool {
69        u8::from(*self) >= 0x80
70    }
71
72    #[must_use]
73    pub fn from_u8(value: u8) -> Option<Self> {
74        match value {
75            0x00 => Some(Self::Success),
76            0x01 => Some(Self::GrantedQoS1),
77            0x02 => Some(Self::GrantedQoS2),
78            0x04 => Some(Self::DisconnectWithWillMessage),
79            0x10 => Some(Self::NoMatchingSubscribers),
80            0x11 => Some(Self::NoSubscriptionExisted),
81            0x18 => Some(Self::ContinueAuthentication),
82            0x19 => Some(Self::ReAuthenticate),
83            0x80 => Some(Self::UnspecifiedError),
84            0x81 => Some(Self::MalformedPacket),
85            0x82 => Some(Self::ProtocolError),
86            0x83 => Some(Self::ImplementationSpecificError),
87            0x84 => Some(Self::UnsupportedProtocolVersion),
88            0x85 => Some(Self::ClientIdentifierNotValid),
89            0x86 => Some(Self::BadUsernameOrPassword),
90            0x87 => Some(Self::NotAuthorized),
91            0x88 => Some(Self::ServerUnavailable),
92            0x89 => Some(Self::ServerBusy),
93            0x8A => Some(Self::Banned),
94            0x8B => Some(Self::ServerShuttingDown),
95            0x8C => Some(Self::BadAuthenticationMethod),
96            0x8D => Some(Self::KeepAliveTimeout),
97            0x8E => Some(Self::SessionTakenOver),
98            0x8F => Some(Self::TopicFilterInvalid),
99            0x90 => Some(Self::TopicNameInvalid),
100            0x91 => Some(Self::PacketIdentifierInUse),
101            0x92 => Some(Self::PacketIdentifierNotFound),
102            0x93 => Some(Self::ReceiveMaximumExceeded),
103            0x94 => Some(Self::TopicAliasInvalid),
104            0x95 => Some(Self::PacketTooLarge),
105            0x96 => Some(Self::MessageRateTooHigh),
106            0x97 => Some(Self::QuotaExceeded),
107            0x98 => Some(Self::AdministrativeAction),
108            0x99 => Some(Self::PayloadFormatInvalid),
109            0x9A => Some(Self::RetainNotSupported),
110            0x9B => Some(Self::QoSNotSupported),
111            0x9C => Some(Self::UseAnotherServer),
112            0x9D => Some(Self::ServerMoved),
113            0x9E => Some(Self::SharedSubscriptionsNotSupported),
114            0x9F => Some(Self::ConnectionRateExceeded),
115            0xA0 => Some(Self::MaximumConnectTime),
116            0xA1 => Some(Self::SubscriptionIdentifiersNotSupported),
117            0xA2 => Some(Self::WildcardSubscriptionsNotSupported),
118            _ => None,
119        }
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    #[test]
128    fn test_reason_code_success_check() {
129        assert!(ReasonCode::Success.is_success());
130        assert!(ReasonCode::GrantedQoS1.is_success());
131        assert!(ReasonCode::GrantedQoS2.is_success());
132        assert!(ReasonCode::NoMatchingSubscribers.is_success());
133
134        assert!(!ReasonCode::UnspecifiedError.is_success());
135        assert!(!ReasonCode::MalformedPacket.is_success());
136        assert!(!ReasonCode::NotAuthorized.is_success());
137    }
138
139    #[test]
140    fn test_reason_code_error_check() {
141        assert!(!ReasonCode::Success.is_error());
142        assert!(!ReasonCode::GrantedQoS1.is_error());
143
144        assert!(ReasonCode::UnspecifiedError.is_error());
145        assert!(ReasonCode::MalformedPacket.is_error());
146        assert!(ReasonCode::ProtocolError.is_error());
147        assert!(ReasonCode::ServerBusy.is_error());
148    }
149
150    #[test]
151    fn test_reason_code_from_u8() {
152        assert_eq!(ReasonCode::from_u8(0x00), Some(ReasonCode::Success));
153        assert_eq!(ReasonCode::from_u8(0x01), Some(ReasonCode::GrantedQoS1));
154        assert_eq!(ReasonCode::from_u8(0x02), Some(ReasonCode::GrantedQoS2));
155        assert_eq!(
156            ReasonCode::from_u8(0x80),
157            Some(ReasonCode::UnspecifiedError)
158        );
159        assert_eq!(ReasonCode::from_u8(0x81), Some(ReasonCode::MalformedPacket));
160        assert_eq!(ReasonCode::from_u8(0x87), Some(ReasonCode::NotAuthorized));
161        assert_eq!(
162            ReasonCode::from_u8(0xA2),
163            Some(ReasonCode::WildcardSubscriptionsNotSupported)
164        );
165
166        // Invalid codes
167        assert_eq!(ReasonCode::from_u8(0x03), None);
168        assert_eq!(ReasonCode::from_u8(0x05), None);
169        assert_eq!(ReasonCode::from_u8(0xFF), None);
170    }
171
172    #[test]
173    fn test_reason_code_aliases() {
174        assert_eq!(NORMAL_DISCONNECTION, ReasonCode::Success);
175        assert_eq!(GRANTED_QOS_0, ReasonCode::Success);
176        assert_eq!(NORMAL_DISCONNECTION as u8, 0x00);
177        assert_eq!(GRANTED_QOS_0 as u8, 0x00);
178    }
179
180    #[test]
181    fn test_reason_code_values() {
182        // Test specific values to ensure correct assignment
183        assert_eq!(ReasonCode::Success as u8, 0x00);
184        assert_eq!(ReasonCode::DisconnectWithWillMessage as u8, 0x04);
185        assert_eq!(ReasonCode::NoMatchingSubscribers as u8, 0x10);
186        assert_eq!(ReasonCode::UnspecifiedError as u8, 0x80);
187        assert_eq!(ReasonCode::ClientIdentifierNotValid as u8, 0x85);
188        assert_eq!(ReasonCode::ServerBusy as u8, 0x89);
189        assert_eq!(ReasonCode::QuotaExceeded as u8, 0x97);
190        assert_eq!(ReasonCode::WildcardSubscriptionsNotSupported as u8, 0xA2);
191    }
192}