Skip to main content

mqtt5_protocol/
quic_error_codes.rs

1use core::fmt;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
4pub enum QuicConnectionCode {
5    NoError = 0x00,
6    TlsError = 0xB1,
7    Unspecified = 0xB2,
8    TooManyRecoverAttempts = 0xB3,
9    ProtocolLevel0 = 0xB4,
10}
11
12impl QuicConnectionCode {
13    #[must_use]
14    pub fn code(self) -> u32 {
15        self as u32
16    }
17
18    #[must_use]
19    pub fn from_code(value: u32) -> Option<Self> {
20        match value {
21            0x00 => Some(Self::NoError),
22            0xB1 => Some(Self::TlsError),
23            0xB2 => Some(Self::Unspecified),
24            0xB3 => Some(Self::TooManyRecoverAttempts),
25            0xB4 => Some(Self::ProtocolLevel0),
26            _ => None,
27        }
28    }
29}
30
31impl fmt::Display for QuicConnectionCode {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        match self {
34            Self::NoError => write!(f, "NO_ERROR"),
35            Self::TlsError => write!(f, "ERROR_TLS_ERROR"),
36            Self::Unspecified => write!(f, "ERROR_UNSPECIFIED"),
37            Self::TooManyRecoverAttempts => write!(f, "ERROR_TOO_MANY_RECOVER_ATTEMPTS"),
38            Self::ProtocolLevel0 => write!(f, "ERROR_PROTOCOL_L0"),
39        }
40    }
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
44pub enum QuicStreamCode {
45    NoError = 0x00,
46    NoFlowState = 0xB3,
47    NotFlowOwner = 0xB4,
48    StreamType = 0xB5,
49    BadFlowId = 0xB6,
50    PersistentTopic = 0xB7,
51    PersistentSub = 0xB8,
52    OptionalHeader = 0xB9,
53    IncompletePacket = 0xBA,
54    FlowOpenIdle = 0xBB,
55    FlowCancelled = 0xBC,
56    FlowPacketCancelled = 0xBD,
57    FlowRefused = 0xBE,
58    DiscardState = 0xBF,
59    ServerPushNotWelcome = 0xC0,
60    RecoveryFailed = 0xC1,
61}
62
63impl QuicStreamCode {
64    #[must_use]
65    pub fn code(self) -> u32 {
66        self as u32
67    }
68
69    #[must_use]
70    pub fn from_code(value: u32) -> Option<Self> {
71        match value {
72            0x00 => Some(Self::NoError),
73            0xB3 => Some(Self::NoFlowState),
74            0xB4 => Some(Self::NotFlowOwner),
75            0xB5 => Some(Self::StreamType),
76            0xB6 => Some(Self::BadFlowId),
77            0xB7 => Some(Self::PersistentTopic),
78            0xB8 => Some(Self::PersistentSub),
79            0xB9 => Some(Self::OptionalHeader),
80            0xBA => Some(Self::IncompletePacket),
81            0xBB => Some(Self::FlowOpenIdle),
82            0xBC => Some(Self::FlowCancelled),
83            0xBD => Some(Self::FlowPacketCancelled),
84            0xBE => Some(Self::FlowRefused),
85            0xBF => Some(Self::DiscardState),
86            0xC0 => Some(Self::ServerPushNotWelcome),
87            0xC1 => Some(Self::RecoveryFailed),
88            _ => None,
89        }
90    }
91
92    #[must_use]
93    pub fn error_level(self) -> Option<u8> {
94        match self {
95            Self::NoError => None,
96            Self::NoFlowState | Self::NotFlowOwner => Some(0),
97            Self::StreamType
98            | Self::BadFlowId
99            | Self::PersistentTopic
100            | Self::PersistentSub
101            | Self::OptionalHeader
102            | Self::DiscardState
103            | Self::ServerPushNotWelcome
104            | Self::RecoveryFailed => Some(1),
105            Self::IncompletePacket
106            | Self::FlowOpenIdle
107            | Self::FlowCancelled
108            | Self::FlowPacketCancelled
109            | Self::FlowRefused => Some(2),
110        }
111    }
112
113    #[must_use]
114    pub fn should_discard_state(self) -> bool {
115        matches!(
116            self,
117            Self::BadFlowId
118                | Self::PersistentSub
119                | Self::OptionalHeader
120                | Self::FlowCancelled
121                | Self::DiscardState
122                | Self::ServerPushNotWelcome
123                | Self::RecoveryFailed
124        )
125    }
126}
127
128impl fmt::Display for QuicStreamCode {
129    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130        match self {
131            Self::NoError => write!(f, "NO_ERROR"),
132            Self::NoFlowState => write!(f, "ERROR_NO_FLOW_STATE"),
133            Self::NotFlowOwner => write!(f, "ERROR_NOT_FLOW_OWNER"),
134            Self::StreamType => write!(f, "ERROR_STREAM_TYPE"),
135            Self::BadFlowId => write!(f, "ERROR_BAD_FLOW_ID"),
136            Self::PersistentTopic => write!(f, "ERROR_PERSISTENT_TOPIC"),
137            Self::PersistentSub => write!(f, "ERROR_PERSISTENT_SUB"),
138            Self::OptionalHeader => write!(f, "ERROR_OPTIONAL_HEADER"),
139            Self::IncompletePacket => write!(f, "ERROR_INCOMPLETE_PACKET"),
140            Self::FlowOpenIdle => write!(f, "ERROR_FLOW_OPEN_IDLE"),
141            Self::FlowCancelled => write!(f, "ERROR_FLOW_CANCELLED"),
142            Self::FlowPacketCancelled => write!(f, "ERROR_FLOW_PACKET_CANCELLED"),
143            Self::FlowRefused => write!(f, "ERROR_FLOW_REFUSED"),
144            Self::DiscardState => write!(f, "ERROR_DISCARD_STATE"),
145            Self::ServerPushNotWelcome => write!(f, "ERROR_SERVER_PUSH_NOT_WELCOME"),
146            Self::RecoveryFailed => write!(f, "ERROR_RECOVERY_FAILED"),
147        }
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    #[test]
156    fn test_connection_code_round_trip() {
157        let codes = [
158            QuicConnectionCode::NoError,
159            QuicConnectionCode::TlsError,
160            QuicConnectionCode::Unspecified,
161            QuicConnectionCode::TooManyRecoverAttempts,
162            QuicConnectionCode::ProtocolLevel0,
163        ];
164        for code in codes {
165            let value = code.code();
166            let parsed = QuicConnectionCode::from_code(value);
167            assert_eq!(parsed, Some(code), "round-trip failed for {code}");
168        }
169    }
170
171    #[test]
172    fn test_connection_code_values() {
173        assert_eq!(QuicConnectionCode::NoError.code(), 0x00);
174        assert_eq!(QuicConnectionCode::TlsError.code(), 0xB1);
175        assert_eq!(QuicConnectionCode::Unspecified.code(), 0xB2);
176        assert_eq!(QuicConnectionCode::TooManyRecoverAttempts.code(), 0xB3);
177        assert_eq!(QuicConnectionCode::ProtocolLevel0.code(), 0xB4);
178    }
179
180    #[test]
181    fn test_connection_code_unknown() {
182        assert_eq!(QuicConnectionCode::from_code(0xFF), None);
183        assert_eq!(QuicConnectionCode::from_code(0xB5), None);
184    }
185
186    #[test]
187    fn test_stream_code_round_trip() {
188        let codes = [
189            QuicStreamCode::NoError,
190            QuicStreamCode::NoFlowState,
191            QuicStreamCode::NotFlowOwner,
192            QuicStreamCode::StreamType,
193            QuicStreamCode::BadFlowId,
194            QuicStreamCode::PersistentTopic,
195            QuicStreamCode::PersistentSub,
196            QuicStreamCode::OptionalHeader,
197            QuicStreamCode::IncompletePacket,
198            QuicStreamCode::FlowOpenIdle,
199            QuicStreamCode::FlowCancelled,
200            QuicStreamCode::FlowPacketCancelled,
201            QuicStreamCode::FlowRefused,
202            QuicStreamCode::DiscardState,
203            QuicStreamCode::ServerPushNotWelcome,
204            QuicStreamCode::RecoveryFailed,
205        ];
206        for code in codes {
207            let value = code.code();
208            let parsed = QuicStreamCode::from_code(value);
209            assert_eq!(parsed, Some(code), "round-trip failed for {code}");
210        }
211    }
212
213    #[test]
214    fn test_stream_code_values() {
215        assert_eq!(QuicStreamCode::NoError.code(), 0x00);
216        assert_eq!(QuicStreamCode::NoFlowState.code(), 0xB3);
217        assert_eq!(QuicStreamCode::NotFlowOwner.code(), 0xB4);
218        assert_eq!(QuicStreamCode::StreamType.code(), 0xB5);
219        assert_eq!(QuicStreamCode::BadFlowId.code(), 0xB6);
220        assert_eq!(QuicStreamCode::PersistentTopic.code(), 0xB7);
221        assert_eq!(QuicStreamCode::PersistentSub.code(), 0xB8);
222        assert_eq!(QuicStreamCode::OptionalHeader.code(), 0xB9);
223        assert_eq!(QuicStreamCode::IncompletePacket.code(), 0xBA);
224        assert_eq!(QuicStreamCode::FlowOpenIdle.code(), 0xBB);
225        assert_eq!(QuicStreamCode::FlowCancelled.code(), 0xBC);
226        assert_eq!(QuicStreamCode::FlowPacketCancelled.code(), 0xBD);
227        assert_eq!(QuicStreamCode::FlowRefused.code(), 0xBE);
228        assert_eq!(QuicStreamCode::DiscardState.code(), 0xBF);
229        assert_eq!(QuicStreamCode::ServerPushNotWelcome.code(), 0xC0);
230        assert_eq!(QuicStreamCode::RecoveryFailed.code(), 0xC1);
231    }
232
233    #[test]
234    fn test_stream_code_unknown() {
235        assert_eq!(QuicStreamCode::from_code(0xFF), None);
236        assert_eq!(QuicStreamCode::from_code(0xB1), None);
237    }
238
239    #[test]
240    fn test_stream_error_levels() {
241        assert_eq!(QuicStreamCode::NoError.error_level(), None);
242        assert_eq!(QuicStreamCode::NoFlowState.error_level(), Some(0));
243        assert_eq!(QuicStreamCode::NotFlowOwner.error_level(), Some(0));
244        assert_eq!(QuicStreamCode::StreamType.error_level(), Some(1));
245        assert_eq!(QuicStreamCode::BadFlowId.error_level(), Some(1));
246        assert_eq!(QuicStreamCode::PersistentTopic.error_level(), Some(1));
247        assert_eq!(QuicStreamCode::PersistentSub.error_level(), Some(1));
248        assert_eq!(QuicStreamCode::OptionalHeader.error_level(), Some(1));
249        assert_eq!(QuicStreamCode::DiscardState.error_level(), Some(1));
250        assert_eq!(QuicStreamCode::ServerPushNotWelcome.error_level(), Some(1));
251        assert_eq!(QuicStreamCode::RecoveryFailed.error_level(), Some(1));
252        assert_eq!(QuicStreamCode::IncompletePacket.error_level(), Some(2));
253        assert_eq!(QuicStreamCode::FlowOpenIdle.error_level(), Some(2));
254        assert_eq!(QuicStreamCode::FlowCancelled.error_level(), Some(2));
255        assert_eq!(QuicStreamCode::FlowPacketCancelled.error_level(), Some(2));
256        assert_eq!(QuicStreamCode::FlowRefused.error_level(), Some(2));
257    }
258
259    #[test]
260    fn test_stream_should_discard_state() {
261        assert!(!QuicStreamCode::NoError.should_discard_state());
262        assert!(!QuicStreamCode::NoFlowState.should_discard_state());
263        assert!(!QuicStreamCode::NotFlowOwner.should_discard_state());
264        assert!(!QuicStreamCode::StreamType.should_discard_state());
265        assert!(QuicStreamCode::BadFlowId.should_discard_state());
266        assert!(!QuicStreamCode::PersistentTopic.should_discard_state());
267        assert!(QuicStreamCode::PersistentSub.should_discard_state());
268        assert!(QuicStreamCode::OptionalHeader.should_discard_state());
269        assert!(!QuicStreamCode::IncompletePacket.should_discard_state());
270        assert!(!QuicStreamCode::FlowOpenIdle.should_discard_state());
271        assert!(QuicStreamCode::FlowCancelled.should_discard_state());
272        assert!(!QuicStreamCode::FlowPacketCancelled.should_discard_state());
273        assert!(!QuicStreamCode::FlowRefused.should_discard_state());
274        assert!(QuicStreamCode::DiscardState.should_discard_state());
275        assert!(QuicStreamCode::ServerPushNotWelcome.should_discard_state());
276        assert!(QuicStreamCode::RecoveryFailed.should_discard_state());
277    }
278
279    #[test]
280    fn test_connection_code_display() {
281        assert_eq!(QuicConnectionCode::NoError.to_string(), "NO_ERROR");
282        assert_eq!(QuicConnectionCode::TlsError.to_string(), "ERROR_TLS_ERROR");
283        assert_eq!(
284            QuicConnectionCode::Unspecified.to_string(),
285            "ERROR_UNSPECIFIED"
286        );
287        assert_eq!(
288            QuicConnectionCode::TooManyRecoverAttempts.to_string(),
289            "ERROR_TOO_MANY_RECOVER_ATTEMPTS"
290        );
291        assert_eq!(
292            QuicConnectionCode::ProtocolLevel0.to_string(),
293            "ERROR_PROTOCOL_L0"
294        );
295    }
296
297    #[test]
298    fn test_stream_code_display() {
299        assert_eq!(
300            QuicStreamCode::NoFlowState.to_string(),
301            "ERROR_NO_FLOW_STATE"
302        );
303        assert_eq!(
304            QuicStreamCode::RecoveryFailed.to_string(),
305            "ERROR_RECOVERY_FAILED"
306        );
307    }
308
309    #[test]
310    fn test_namespace_overlap_independence() {
311        assert_eq!(QuicConnectionCode::TooManyRecoverAttempts.code(), 0xB3);
312        assert_eq!(QuicStreamCode::NoFlowState.code(), 0xB3);
313
314        assert_eq!(QuicConnectionCode::ProtocolLevel0.code(), 0xB4);
315        assert_eq!(QuicStreamCode::NotFlowOwner.code(), 0xB4);
316    }
317}