http/status.rs
1//! HTTP status codes
2//!
3//! This module contains HTTP-status code related structs and errors. The main
4//! type in this module is `StatusCode` which is not intended to be used through
5//! this module but rather the `http::StatusCode` type.
6//!
7//! # Examples
8//!
9//! ```
10//! use http::StatusCode;
11//!
12//! assert_eq!(StatusCode::from_u16(200).unwrap(), StatusCode::OK);
13//! assert_eq!(StatusCode::NOT_FOUND, 404);
14//! assert!(StatusCode::OK.is_success());
15//! ```
16
17use std::convert::TryFrom;
18use std::error::Error;
19use std::fmt;
20use std::num::NonZeroU16;
21use std::str::FromStr;
22
23/// An HTTP status code (`status-code` in RFC 9110 et al.).
24///
25/// Constants are provided for known status codes, including those in the IANA
26/// [HTTP Status Code Registry](
27/// https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml).
28///
29/// Status code values in the range 100-999 (inclusive) are supported by this
30/// type. Values in the range 100-599 are semantically classified by the most
31/// significant digit. See [`StatusCode::is_success`], etc. Values above 599
32/// are unclassified but allowed for legacy compatibility, though their use is
33/// discouraged. Applications may interpret such values as protocol errors.
34///
35/// # Examples
36///
37/// ```
38/// use http::StatusCode;
39///
40/// assert_eq!(StatusCode::from_u16(200).unwrap(), StatusCode::OK);
41/// assert_eq!(StatusCode::NOT_FOUND.as_u16(), 404);
42/// assert!(StatusCode::OK.is_success());
43/// ```
44#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
45pub struct StatusCode(NonZeroU16);
46
47/// A possible error value when converting a `StatusCode` from a `u16` or `&str`.
48///
49/// This error indicates that the supplied input was not a valid number, was less
50/// than 100, or was greater than 999.
51pub struct InvalidStatusCode {
52 _priv: (),
53}
54
55impl StatusCode {
56 /// Converts a u16 to a status code.
57 ///
58 /// The function validates the correctness of the supplied u16. It must be
59 /// greater or equal to 100 and less than 1000.
60 ///
61 /// # Example
62 ///
63 /// ```
64 /// use http::StatusCode;
65 ///
66 /// let ok = StatusCode::from_u16(200).unwrap();
67 /// assert_eq!(ok, StatusCode::OK);
68 ///
69 /// let err = StatusCode::from_u16(99);
70 /// assert!(err.is_err());
71 /// ```
72 #[inline]
73 pub const fn from_u16(src: u16) -> Result<StatusCode, InvalidStatusCode> {
74 if let 100..=999 = src {
75 if let Some(code) = NonZeroU16::new(src) {
76 return Ok(StatusCode(code));
77 }
78 }
79 Err(InvalidStatusCode::new())
80 }
81
82 /// Converts a `&[u8]` to a status code.
83 pub fn from_bytes(src: &[u8]) -> Result<StatusCode, InvalidStatusCode> {
84 if src.len() != 3 {
85 return Err(InvalidStatusCode::new());
86 }
87
88 let a = src[0].wrapping_sub(b'0') as u16;
89 let b = src[1].wrapping_sub(b'0') as u16;
90 let c = src[2].wrapping_sub(b'0') as u16;
91
92 if a == 0 || a > 9 || b > 9 || c > 9 {
93 return Err(InvalidStatusCode::new());
94 }
95
96 let status = (a * 100) + (b * 10) + c;
97 NonZeroU16::new(status)
98 .map(StatusCode)
99 .ok_or_else(InvalidStatusCode::new)
100 }
101
102 /// Returns the `u16` corresponding to this `StatusCode`.
103 ///
104 /// # Note
105 ///
106 /// This is the same as the `From<StatusCode>` implementation, but
107 /// included as an inherent method because that implementation doesn't
108 /// appear in rustdocs, as well as a way to force the type instead of
109 /// relying on inference.
110 ///
111 /// # Example
112 ///
113 /// ```
114 /// let status = http::StatusCode::OK;
115 /// assert_eq!(status.as_u16(), 200);
116 /// ```
117 #[inline]
118 pub const fn as_u16(&self) -> u16 {
119 self.0.get()
120 }
121
122 /// Returns a &str representation of the `StatusCode`
123 ///
124 /// The return value only includes a numerical representation of the
125 /// status code. The canonical reason is not included.
126 ///
127 /// # Example
128 ///
129 /// ```
130 /// let status = http::StatusCode::OK;
131 /// assert_eq!(status.as_str(), "200");
132 /// ```
133 #[inline]
134 pub fn as_str(&self) -> &str {
135 let offset = (self.0.get() - 100) as usize;
136 let offset = offset * 3;
137
138 // Invariant: self has checked range [100, 999] and CODE_DIGITS is
139 // ASCII-only, of length 900 * 3 = 2700 bytes
140
141 #[cfg(debug_assertions)]
142 {
143 &CODE_DIGITS[offset..offset + 3]
144 }
145
146 #[cfg(not(debug_assertions))]
147 unsafe {
148 CODE_DIGITS.get_unchecked(offset..offset + 3)
149 }
150 }
151
152 /// Get the standardised `reason-phrase` for this status code.
153 ///
154 /// This is mostly here for servers writing responses, but could potentially have application
155 /// at other times.
156 ///
157 /// The reason phrase is defined as being exclusively for human readers. You should avoid
158 /// deriving any meaning from it at all costs.
159 ///
160 /// Bear in mind also that in HTTP/2.0 and HTTP/3.0 the reason phrase is abolished from
161 /// transmission, and so this canonical reason phrase really is the only reason phrase you’ll
162 /// find.
163 ///
164 /// # Example
165 ///
166 /// ```
167 /// let status = http::StatusCode::OK;
168 /// assert_eq!(status.canonical_reason(), Some("OK"));
169 /// ```
170 pub fn canonical_reason(&self) -> Option<&'static str> {
171 canonical_reason(self.0.get())
172 }
173
174 /// Check if status is within 100-199.
175 #[inline]
176 pub fn is_informational(&self) -> bool {
177 (100..200).contains(&self.0.get())
178 }
179
180 /// Check if status is within 200-299.
181 #[inline]
182 pub fn is_success(&self) -> bool {
183 (200..300).contains(&self.0.get())
184 }
185
186 /// Check if status is within 300-399.
187 #[inline]
188 pub fn is_redirection(&self) -> bool {
189 (300..400).contains(&self.0.get())
190 }
191
192 /// Check if status is within 400-499.
193 #[inline]
194 pub fn is_client_error(&self) -> bool {
195 (400..500).contains(&self.0.get())
196 }
197
198 /// Check if status is within 500-599.
199 #[inline]
200 pub fn is_server_error(&self) -> bool {
201 (500..600).contains(&self.0.get())
202 }
203}
204
205impl fmt::Debug for StatusCode {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 fmt::Debug::fmt(&self.0, f)
208 }
209}
210
211/// Formats the status code, *including* the canonical reason.
212///
213/// # Example
214///
215/// ```
216/// # use http::StatusCode;
217/// assert_eq!(format!("{}", StatusCode::OK), "200 OK");
218/// ```
219impl fmt::Display for StatusCode {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 write!(
222 f,
223 "{} {}",
224 u16::from(*self),
225 self.canonical_reason().unwrap_or("<unknown status code>")
226 )
227 }
228}
229
230impl Default for StatusCode {
231 #[inline]
232 fn default() -> StatusCode {
233 StatusCode::OK
234 }
235}
236
237impl PartialEq<u16> for StatusCode {
238 #[inline]
239 fn eq(&self, other: &u16) -> bool {
240 self.as_u16() == *other
241 }
242}
243
244impl PartialEq<StatusCode> for u16 {
245 #[inline]
246 fn eq(&self, other: &StatusCode) -> bool {
247 *self == other.as_u16()
248 }
249}
250
251impl From<StatusCode> for u16 {
252 #[inline]
253 fn from(status: StatusCode) -> u16 {
254 status.0.get()
255 }
256}
257
258impl FromStr for StatusCode {
259 type Err = InvalidStatusCode;
260
261 fn from_str(s: &str) -> Result<StatusCode, InvalidStatusCode> {
262 StatusCode::from_bytes(s.as_ref())
263 }
264}
265
266impl<'a> From<&'a StatusCode> for StatusCode {
267 #[inline]
268 fn from(t: &'a StatusCode) -> Self {
269 t.to_owned()
270 }
271}
272
273impl<'a> TryFrom<&'a [u8]> for StatusCode {
274 type Error = InvalidStatusCode;
275
276 #[inline]
277 fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
278 StatusCode::from_bytes(t)
279 }
280}
281
282impl<'a> TryFrom<&'a str> for StatusCode {
283 type Error = InvalidStatusCode;
284
285 #[inline]
286 fn try_from(t: &'a str) -> Result<Self, Self::Error> {
287 t.parse()
288 }
289}
290
291impl TryFrom<u16> for StatusCode {
292 type Error = InvalidStatusCode;
293
294 #[inline]
295 fn try_from(t: u16) -> Result<Self, Self::Error> {
296 StatusCode::from_u16(t)
297 }
298}
299
300macro_rules! status_codes {
301 (
302 $(
303 $(#[$docs:meta])*
304 ($num:expr, $konst:ident, $phrase:expr);
305 )+
306 ) => {
307 impl StatusCode {
308 $(
309 $(#[$docs])*
310 pub const $konst: StatusCode = StatusCode(unsafe { NonZeroU16::new_unchecked($num) });
311 )+
312
313 }
314
315 fn canonical_reason(num: u16) -> Option<&'static str> {
316 match num {
317 $(
318 $num => Some($phrase),
319 )+
320 _ => None
321 }
322 }
323 }
324}
325
326status_codes! {
327 /// 100 Continue
328 /// [[RFC9110, Section 15.2.1](https://datatracker.ietf.org/doc/html/rfc9110#section-15.2.1)]
329 (100, CONTINUE, "Continue");
330 /// 101 Switching Protocols
331 /// [[RFC9110, Section 15.2.2](https://datatracker.ietf.org/doc/html/rfc9110#section-15.2.2)]
332 (101, SWITCHING_PROTOCOLS, "Switching Protocols");
333 /// 102 Processing
334 /// [[RFC2518, Section 10.1](https://datatracker.ietf.org/doc/html/rfc2518#section-10.1)]
335 (102, PROCESSING, "Processing");
336 /// 103 Early Hints
337 /// [[RFC8297, Section 2](https://datatracker.ietf.org/doc/html/rfc8297#section-2)]
338 (103, EARLY_HINTS, "Early Hints");
339
340 /// 200 OK
341 /// [[RFC9110, Section 15.3.1](https://datatracker.ietf.org/doc/html/rfc9110#section-15.3.1)]
342 (200, OK, "OK");
343 /// 201 Created
344 /// [[RFC9110, Section 15.3.2](https://datatracker.ietf.org/doc/html/rfc9110#section-15.3.2)]
345 (201, CREATED, "Created");
346 /// 202 Accepted
347 /// [[RFC9110, Section 15.3.3](https://datatracker.ietf.org/doc/html/rfc9110#section-15.3.3)]
348 (202, ACCEPTED, "Accepted");
349 /// 203 Non-Authoritative Information
350 /// [[RFC9110, Section 15.3.4](https://datatracker.ietf.org/doc/html/rfc9110#section-15.3.4)]
351 (203, NON_AUTHORITATIVE_INFORMATION, "Non Authoritative Information");
352 /// 204 No Content
353 /// [[RFC9110, Section 15.3.5](https://datatracker.ietf.org/doc/html/rfc9110#section-15.3.5)]
354 (204, NO_CONTENT, "No Content");
355 /// 205 Reset Content
356 /// [[RFC9110, Section 15.3.6](https://datatracker.ietf.org/doc/html/rfc9110#section-15.3.6)]
357 (205, RESET_CONTENT, "Reset Content");
358 /// 206 Partial Content
359 /// [[RFC9110, Section 15.3.7](https://datatracker.ietf.org/doc/html/rfc9110#section-15.3.7)]
360 (206, PARTIAL_CONTENT, "Partial Content");
361 /// 207 Multi-Status
362 /// [[RFC4918, Section 11.1](https://datatracker.ietf.org/doc/html/rfc4918#section-11.1)]
363 (207, MULTI_STATUS, "Multi-Status");
364 /// 208 Already Reported
365 /// [[RFC5842, Section 7.1](https://datatracker.ietf.org/doc/html/rfc5842#section-7.1)]
366 (208, ALREADY_REPORTED, "Already Reported");
367
368 /// 226 IM Used
369 /// [[RFC3229, Section 10.4.1](https://datatracker.ietf.org/doc/html/rfc3229#section-10.4.1)]
370 (226, IM_USED, "IM Used");
371
372 /// 300 Multiple Choices
373 /// [[RFC9110, Section 15.4.1](https://datatracker.ietf.org/doc/html/rfc9110#section-15.4.1)]
374 (300, MULTIPLE_CHOICES, "Multiple Choices");
375 /// 301 Moved Permanently
376 /// [[RFC9110, Section 15.4.2](https://datatracker.ietf.org/doc/html/rfc9110#section-15.4.2)]
377 (301, MOVED_PERMANENTLY, "Moved Permanently");
378 /// 302 Found
379 /// [[RFC9110, Section 15.4.3](https://datatracker.ietf.org/doc/html/rfc9110#section-15.4.3)]
380 (302, FOUND, "Found");
381 /// 303 See Other
382 /// [[RFC9110, Section 15.4.4](https://datatracker.ietf.org/doc/html/rfc9110#section-15.4.4)]
383 (303, SEE_OTHER, "See Other");
384 /// 304 Not Modified
385 /// [[RFC9110, Section 15.4.5](https://datatracker.ietf.org/doc/html/rfc9110#section-15.4.5)]
386 (304, NOT_MODIFIED, "Not Modified");
387 /// 305 Use Proxy
388 /// [[RFC9110, Section 15.4.6](https://datatracker.ietf.org/doc/html/rfc9110#section-15.4.6)]
389 (305, USE_PROXY, "Use Proxy");
390 /// 307 Temporary Redirect
391 /// [[RFC9110, Section 15.4.7](https://datatracker.ietf.org/doc/html/rfc9110#section-15.4.7)]
392 (307, TEMPORARY_REDIRECT, "Temporary Redirect");
393 /// 308 Permanent Redirect
394 /// [[RFC9110, Section 15.4.8](https://datatracker.ietf.org/doc/html/rfc9110#section-15.4.8)]
395 (308, PERMANENT_REDIRECT, "Permanent Redirect");
396
397 /// 400 Bad Request
398 /// [[RFC9110, Section 15.5.1](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.1)]
399 (400, BAD_REQUEST, "Bad Request");
400 /// 401 Unauthorized
401 /// [[RFC9110, Section 15.5.2](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.2)]
402 (401, UNAUTHORIZED, "Unauthorized");
403 /// 402 Payment Required
404 /// [[RFC9110, Section 15.5.3](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.3)]
405 (402, PAYMENT_REQUIRED, "Payment Required");
406 /// 403 Forbidden
407 /// [[RFC9110, Section 15.5.4](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.4)]
408 (403, FORBIDDEN, "Forbidden");
409 /// 404 Not Found
410 /// [[RFC9110, Section 15.5.5](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.5)]
411 (404, NOT_FOUND, "Not Found");
412 /// 405 Method Not Allowed
413 /// [[RFC9110, Section 15.5.6](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.6)]
414 (405, METHOD_NOT_ALLOWED, "Method Not Allowed");
415 /// 406 Not Acceptable
416 /// [[RFC9110, Section 15.5.7](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.7)]
417 (406, NOT_ACCEPTABLE, "Not Acceptable");
418 /// 407 Proxy Authentication Required
419 /// [[RFC9110, Section 15.5.8](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.8)]
420 (407, PROXY_AUTHENTICATION_REQUIRED, "Proxy Authentication Required");
421 /// 408 Request Timeout
422 /// [[RFC9110, Section 15.5.9](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.9)]
423 (408, REQUEST_TIMEOUT, "Request Timeout");
424 /// 409 Conflict
425 /// [[RFC9110, Section 15.5.10](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.10)]
426 (409, CONFLICT, "Conflict");
427 /// 410 Gone
428 /// [[RFC9110, Section 15.5.11](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.11)]
429 (410, GONE, "Gone");
430 /// 411 Length Required
431 /// [[RFC9110, Section 15.5.12](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.12)]
432 (411, LENGTH_REQUIRED, "Length Required");
433 /// 412 Precondition Failed
434 /// [[RFC9110, Section 15.5.13](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.13)]
435 (412, PRECONDITION_FAILED, "Precondition Failed");
436 /// 413 Payload Too Large
437 /// [[RFC9110, Section 15.5.14](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.14)]
438 (413, PAYLOAD_TOO_LARGE, "Payload Too Large");
439 /// 414 URI Too Long
440 /// [[RFC9110, Section 15.5.15](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.15)]
441 (414, URI_TOO_LONG, "URI Too Long");
442 /// 415 Unsupported Media Type
443 /// [[RFC9110, Section 15.5.16](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.16)]
444 (415, UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type");
445 /// 416 Range Not Satisfiable
446 /// [[RFC9110, Section 15.5.17](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.17)]
447 (416, RANGE_NOT_SATISFIABLE, "Range Not Satisfiable");
448 /// 417 Expectation Failed
449 /// [[RFC9110, Section 15.5.18](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.18)]
450 (417, EXPECTATION_FAILED, "Expectation Failed");
451 /// 418 I'm a teapot
452 /// [curiously not registered by IANA but [RFC2324, Section 2.3.2](https://datatracker.ietf.org/doc/html/rfc2324#section-2.3.2)]
453 (418, IM_A_TEAPOT, "I'm a teapot");
454
455 /// 421 Misdirected Request
456 /// [[RFC9110, Section 15.5.20](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.20)]
457 (421, MISDIRECTED_REQUEST, "Misdirected Request");
458 /// 422 Unprocessable Entity
459 /// [[RFC9110, Section 15.5.21](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.21)]
460 (422, UNPROCESSABLE_ENTITY, "Unprocessable Entity");
461 /// 423 Locked
462 /// [[RFC4918, Section 11.3](https://datatracker.ietf.org/doc/html/rfc4918#section-11.3)]
463 (423, LOCKED, "Locked");
464 /// 424 Failed Dependency
465 /// [[RFC4918, Section 11.4](https://tools.ietf.org/html/rfc4918#section-11.4)]
466 (424, FAILED_DEPENDENCY, "Failed Dependency");
467
468 /// 425 Too early
469 /// [[RFC8470, Section 5.2](https://httpwg.org/specs/rfc8470.html#status)]
470 (425, TOO_EARLY, "Too Early");
471
472 /// 426 Upgrade Required
473 /// [[RFC9110, Section 15.5.22](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.22)]
474 (426, UPGRADE_REQUIRED, "Upgrade Required");
475
476 /// 428 Precondition Required
477 /// [[RFC6585, Section 3](https://datatracker.ietf.org/doc/html/rfc6585#section-3)]
478 (428, PRECONDITION_REQUIRED, "Precondition Required");
479 /// 429 Too Many Requests
480 /// [[RFC6585, Section 4](https://datatracker.ietf.org/doc/html/rfc6585#section-4)]
481 (429, TOO_MANY_REQUESTS, "Too Many Requests");
482
483 /// 431 Request Header Fields Too Large
484 /// [[RFC6585, Section 5](https://datatracker.ietf.org/doc/html/rfc6585#section-5)]
485 (431, REQUEST_HEADER_FIELDS_TOO_LARGE, "Request Header Fields Too Large");
486
487 /// 451 Unavailable For Legal Reasons
488 /// [[RFC7725, Section 3](https://tools.ietf.org/html/rfc7725#section-3)]
489 (451, UNAVAILABLE_FOR_LEGAL_REASONS, "Unavailable For Legal Reasons");
490
491 /// 500 Internal Server Error
492 /// [[RFC9110, Section 15.6.1](https://datatracker.ietf.org/doc/html/rfc9110#section-15.6.1)]
493 (500, INTERNAL_SERVER_ERROR, "Internal Server Error");
494 /// 501 Not Implemented
495 /// [[RFC9110, Section 15.6.2](https://datatracker.ietf.org/doc/html/rfc9110#section-15.6.2)]
496 (501, NOT_IMPLEMENTED, "Not Implemented");
497 /// 502 Bad Gateway
498 /// [[RFC9110, Section 15.6.3](https://datatracker.ietf.org/doc/html/rfc9110#section-15.6.3)]
499 (502, BAD_GATEWAY, "Bad Gateway");
500 /// 503 Service Unavailable
501 /// [[RFC9110, Section 15.6.4](https://datatracker.ietf.org/doc/html/rfc9110#section-15.6.4)]
502 (503, SERVICE_UNAVAILABLE, "Service Unavailable");
503 /// 504 Gateway Timeout
504 /// [[RFC9110, Section 15.6.5](https://datatracker.ietf.org/doc/html/rfc9110#section-15.6.5)]
505 (504, GATEWAY_TIMEOUT, "Gateway Timeout");
506 /// 505 HTTP Version Not Supported
507 /// [[RFC9110, Section 15.6.6](https://datatracker.ietf.org/doc/html/rfc9110#section-15.6.6)]
508 (505, HTTP_VERSION_NOT_SUPPORTED, "HTTP Version Not Supported");
509 /// 506 Variant Also Negotiates
510 /// [[RFC2295, Section 8.1](https://datatracker.ietf.org/doc/html/rfc2295#section-8.1)]
511 (506, VARIANT_ALSO_NEGOTIATES, "Variant Also Negotiates");
512 /// 507 Insufficient Storage
513 /// [[RFC4918, Section 11.5](https://datatracker.ietf.org/doc/html/rfc4918#section-11.5)]
514 (507, INSUFFICIENT_STORAGE, "Insufficient Storage");
515 /// 508 Loop Detected
516 /// [[RFC5842, Section 7.2](https://datatracker.ietf.org/doc/html/rfc5842#section-7.2)]
517 (508, LOOP_DETECTED, "Loop Detected");
518
519 /// 510 Not Extended
520 /// [[RFC2774, Section 7](https://datatracker.ietf.org/doc/html/rfc2774#section-7)]
521 (510, NOT_EXTENDED, "Not Extended");
522 /// 511 Network Authentication Required
523 /// [[RFC6585, Section 6](https://datatracker.ietf.org/doc/html/rfc6585#section-6)]
524 (511, NETWORK_AUTHENTICATION_REQUIRED, "Network Authentication Required");
525}
526
527impl InvalidStatusCode {
528 const fn new() -> InvalidStatusCode {
529 InvalidStatusCode { _priv: () }
530 }
531}
532
533impl fmt::Debug for InvalidStatusCode {
534 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
535 f.debug_struct("InvalidStatusCode")
536 // skip _priv noise
537 .finish()
538 }
539}
540
541impl fmt::Display for InvalidStatusCode {
542 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
543 f.write_str("invalid status code")
544 }
545}
546
547impl Error for InvalidStatusCode {}
548
549// A string of packed 3-ASCII-digit status code values for the supported range
550// of [100, 999] (900 codes, 2700 bytes).
551const CODE_DIGITS: &str = "\
552100101102103104105106107108109110111112113114115116117118119\
553120121122123124125126127128129130131132133134135136137138139\
554140141142143144145146147148149150151152153154155156157158159\
555160161162163164165166167168169170171172173174175176177178179\
556180181182183184185186187188189190191192193194195196197198199\
557200201202203204205206207208209210211212213214215216217218219\
558220221222223224225226227228229230231232233234235236237238239\
559240241242243244245246247248249250251252253254255256257258259\
560260261262263264265266267268269270271272273274275276277278279\
561280281282283284285286287288289290291292293294295296297298299\
562300301302303304305306307308309310311312313314315316317318319\
563320321322323324325326327328329330331332333334335336337338339\
564340341342343344345346347348349350351352353354355356357358359\
565360361362363364365366367368369370371372373374375376377378379\
566380381382383384385386387388389390391392393394395396397398399\
567400401402403404405406407408409410411412413414415416417418419\
568420421422423424425426427428429430431432433434435436437438439\
569440441442443444445446447448449450451452453454455456457458459\
570460461462463464465466467468469470471472473474475476477478479\
571480481482483484485486487488489490491492493494495496497498499\
572500501502503504505506507508509510511512513514515516517518519\
573520521522523524525526527528529530531532533534535536537538539\
574540541542543544545546547548549550551552553554555556557558559\
575560561562563564565566567568569570571572573574575576577578579\
576580581582583584585586587588589590591592593594595596597598599\
577600601602603604605606607608609610611612613614615616617618619\
578620621622623624625626627628629630631632633634635636637638639\
579640641642643644645646647648649650651652653654655656657658659\
580660661662663664665666667668669670671672673674675676677678679\
581680681682683684685686687688689690691692693694695696697698699\
582700701702703704705706707708709710711712713714715716717718719\
583720721722723724725726727728729730731732733734735736737738739\
584740741742743744745746747748749750751752753754755756757758759\
585760761762763764765766767768769770771772773774775776777778779\
586780781782783784785786787788789790791792793794795796797798799\
587800801802803804805806807808809810811812813814815816817818819\
588820821822823824825826827828829830831832833834835836837838839\
589840841842843844845846847848849850851852853854855856857858859\
590860861862863864865866867868869870871872873874875876877878879\
591880881882883884885886887888889890891892893894895896897898899\
592900901902903904905906907908909910911912913914915916917918919\
593920921922923924925926927928929930931932933934935936937938939\
594940941942943944945946947948949950951952953954955956957958959\
595960961962963964965966967968969970971972973974975976977978979\
596980981982983984985986987988989990991992993994995996997998999";