1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2pub enum ReasonCode {
3 Success = 0x00, GrantedQoS1 = 0x01,
6 GrantedQoS2 = 0x02,
7 DisconnectWithWillMessage = 0x04,
8 NoMatchingSubscribers = 0x10,
9 NoSubscriptionExisted = 0x11,
10 ContinueAuthentication = 0x18,
11 ReAuthenticate = 0x19,
12
13 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
67pub 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 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}