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}