Skip to content

Instantly share code, notes, and snippets.

@pyfisch
Created January 27, 2015 18:57
Show Gist options
  • Save pyfisch/cceae250981243c33c5e to your computer and use it in GitHub Desktop.
Save pyfisch/cceae250981243c33c5e to your computer and use it in GitHub Desktop.
status.rs
//! HTTP status codes.
use std::fmt;
use std::mem::transmute;
use std::num::{FromPrimitive, ToPrimitive};
use std::cmp::Ordering;
pub use self::StatusCode::*;
pub use self::StatusClass::*;
// TODO: Update docs
/// An HTTP status code (`status-code` in RFC 7230 et al.).
///
/// This enum is absolutely exhaustive, covering all 500 possible values (100–599).
///
/// As this is a C‐style enum with each variant having a corresponding value, you may use the likes
/// of `Continue as u16` to retreive the value `100u16`. Normally, though, you should not need to
/// do any such thing; just use the status code as a `StatusCode`.
///
/// If you encounter a status code that you do not know how to deal with, you should treat it as
/// the `x00` status code—e.g. for code 123, treat it as 100 (Continue). This can be achieved with
/// `self.class().default_code()`:
///
/// ```rust
/// # use hyperprotocol::status::{UnregisteredStatusCode, Continue};
/// assert_eq!(UnregisteredStatusCode(123).class().default_code(), Continue);
/// ```
///
/// IANA maintain the [Hypertext Transfer Protocol (HTTP) Status Code
/// Registry](http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml) which is
/// the source for this enum (with one exception, 418 I'm a teapot, which is inexplicably not
/// in the register).
pub enum StatusCode {
/// 100 Continue
/// [[RFC7231, Section 6.2.1](https://tools.ietf.org/html/rfc7231#section-6.2.1)]
Continue,
/// 101 Switching Protocols
/// [[RFC7231, Section 6.2.2](https://tools.ietf.org/html/rfc7231#section-6.2.2)]
SwitchingProtocols,
/// 102 Processing
/// [[RFC2518](https://tools.ietf.org/html/rfc2518)]
Processing,
/// 200 OK
/// [[RFC7231, Section 6.3.1](https://tools.ietf.org/html/rfc7231#section-6.3.1)]
Ok,
/// 201 Created
/// [[RFC7231, Section 6.3.2](https://tools.ietf.org/html/rfc7231#section-6.3.2)]
Created,
/// 202 Accepted
/// [[RFC7231, Section 6.3.3](https://tools.ietf.org/html/rfc7231#section-6.3.3)]
Accepted,
/// 203 Non-Authoritative Information
/// [[RFC7231, Section 6.3.4](https://tools.ietf.org/html/rfc7231#section-6.3.4)]
NonAuthoritativeInformation,
/// 204 No Content
/// [[RFC7231, Section 6.3.5](https://tools.ietf.org/html/rfc7231#section-6.3.5)]
NoContent,
/// 205 Reset Content
/// [[RFC7231, Section 6.3.6](https://tools.ietf.org/html/rfc7231#section-6.3.6)]
ResetContent,
/// 206 Partial Content
/// [[RFC7233, Section 4.1](https://tools.ietf.org/html/rfc7233#section-4.1)]
PartialContent,
/// 207 Multi-Status
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
MultiStatus,
/// 208 Already Reported
/// [[RFC5842](https://tools.ietf.org/html/rfc5842)]
AlreadyReported,
/// 226 IM Used
/// [[RFC3229](https://tools.ietf.org/html/rfc3229)]
ImUsed,
/// 300 Multiple Choices
/// [[RFC7231, Section 6.4.1](https://tools.ietf.org/html/rfc7231#section-6.4.1)]
MultipleChoices,
/// 301 Moved Permanently
/// [[RFC7231, Section 6.4.2](https://tools.ietf.org/html/rfc7231#section-6.4.2)]
MovedPermanently,
/// 302 Found
/// [[RFC7231, Section 6.4.3](https://tools.ietf.org/html/rfc7231#section-6.4.3)]
Found,
/// 303 See Other
/// [[RFC7231, Section 6.4.4](https://tools.ietf.org/html/rfc7231#section-6.4.4)]
SeeOther,
/// 304 Not Modified
/// [[RFC7232, Section 4.1](https://tools.ietf.org/html/rfc7232#section-4.1)]
NotModified,
/// 305 Use Proxy
/// [[RFC7231, Section 6.4.5](https://tools.ietf.org/html/rfc7231#section-6.4.5)]
UseProxy,
/// 307 Temporary Redirect
/// [[RFC7231, Section 6.4.7](https://tools.ietf.org/html/rfc7231#section-6.4.7)]
TemporaryRedirect,
/// 308 Permanent Redirect
/// [[RFC7238](https://tools.ietf.org/html/rfc7238)]
PermanentRedirect,
/// 400 Bad Request
/// [[RFC7231, Section 6.5.1](https://tools.ietf.org/html/rfc7231#section-6.5.1)]
BadRequest,
/// 401 Unauthorized
/// [[RFC7235, Section 3.1](https://tools.ietf.org/html/rfc7235#section-3.1)]
Unauthorized,
/// 402 Payment Required
/// [[RFC7231, Section 6.5.2](https://tools.ietf.org/html/rfc7231#section-6.5.2)]
PaymentRequired,
/// 403 Forbidden
/// [[RFC7231, Section 6.5.3](https://tools.ietf.org/html/rfc7231#section-6.5.3)]
Forbidden,
/// 404 Not Found
/// [[RFC7231, Section 6.5.4](https://tools.ietf.org/html/rfc7231#section-6.5.4)]
NotFound,
/// 405 Method Not Allowed
/// [[RFC7231, Section 6.5.5](https://tools.ietf.org/html/rfc7231#section-6.5.5)]
MethodNotAllowed,
/// 406 Not Acceptable
/// [[RFC7231, Section 6.5.6](https://tools.ietf.org/html/rfc7231#section-6.5.6)]
NotAcceptable,
/// 407 Proxy Authentication Required
/// [[RFC7235, Section 3.2](https://tools.ietf.org/html/rfc7235#section-3.2)]
ProxyAuthenticationRequired,
/// 408 Request Timeout
/// [[RFC7231, Section 6.5.7](https://tools.ietf.org/html/rfc7231#section-6.5.7)]
RequestTimeout,
/// 409 Conflict
/// [[RFC7231, Section 6.5.8](https://tools.ietf.org/html/rfc7231#section-6.5.8)]
Conflict,
/// 410 Gone
/// [[RFC7231, Section 6.5.9](https://tools.ietf.org/html/rfc7231#section-6.5.9)]
Gone,
/// 411 Length Required
/// [[RFC7231, Section 6.5.10](https://tools.ietf.org/html/rfc7231#section-6.5.10)]
LengthRequired,
/// 412 Precondition Failed
/// [[RFC7232, Section 4.2](https://tools.ietf.org/html/rfc7232#section-4.2)]
PreconditionFailed,
/// 413 Payload Too Large
/// [[RFC7231, Section 6.5.11](https://tools.ietf.org/html/rfc7231#section-6.5.11)]
PayloadTooLarge,
/// 414 URI Too Long
/// [[RFC7231, Section 6.5.12](https://tools.ietf.org/html/rfc7231#section-6.5.12)]
UriTooLong,
/// 415 Unsupported Media Type
/// [[RFC7231, Section 6.5.13](https://tools.ietf.org/html/rfc7231#section-6.5.13)]
UnsupportedMediaType,
/// 416 Range Not Satisfiable
/// [[RFC7233, Section 4.4](https://tools.ietf.org/html/rfc7233#section-4.4)]
RangeNotSatisfiable,
/// 417 Expectation Failed
/// [[RFC7231, Section 6.5.14](https://tools.ietf.org/html/rfc7231#section-6.5.14)]
ExpectationFailed,
/// 418 I'm a teapot
/// [curiously, not registered by IANA, but [RFC2324](https://tools.ietf.org/html/rfc2324)]
ImATeapot,
/// 422 Unprocessable Entity
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
UnprocessableEntity,
/// 423 Locked
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
Locked,
/// 424 Failed Dependency
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
FailedDependency,
/// 426 Upgrade Required
/// [[RFC7231, Section 6.5.15](https://tools.ietf.org/html/rfc7231#section-6.5.15)]
UpgradeRequired,
/// 428 Precondition Required
/// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
PreconditionRequired,
/// 429 Too Many Requests
/// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
TooManyRequests,
/// 431 Request Header Fields Too Large
/// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
RequestHeaderFieldsTooLarge,
/// 500 Internal Server Error
/// [[RFC7231, Section 6.6.1](https://tools.ietf.org/html/rfc7231#section-6.6.1)]
InternalServerError,
/// 501 Not Implemented
/// [[RFC7231, Section 6.6.2](https://tools.ietf.org/html/rfc7231#section-6.6.2)]
NotImplemented,
/// 502 Bad Gateway
/// [[RFC7231, Section 6.6.3](https://tools.ietf.org/html/rfc7231#section-6.6.3)]
BadGateway,
/// 503 Service Unavailable
/// [[RFC7231, Section 6.6.4](https://tools.ietf.org/html/rfc7231#section-6.6.4)]
ServiceUnavailable,
/// 504 Gateway Timeout
/// [[RFC7231, Section 6.6.5](https://tools.ietf.org/html/rfc7231#section-6.6.5)]
GatewayTimeout,
/// 505 HTTP Version Not Supported
/// [[RFC7231, Section 6.6.6](https://tools.ietf.org/html/rfc7231#section-6.6.6)]
HttpVersionNotSupported,
/// 506 Variant Also Negotiates
/// [[RFC2295](https://tools.ietf.org/html/rfc2295)]
VariantAlsoNegotiates,
/// 507 Insufficient Storage
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
InsufficientStorage,
/// 508 Loop Detected
/// [[RFC5842](https://tools.ietf.org/html/rfc5842)]
LoopDetected,
/// 510 Not Extended
/// [[RFC2774](https://tools.ietf.org/html/rfc2774)]
NotExtended,
/// 511 Network Authentication Required
/// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
NetworkAuthenticationRequired,
/// A method not in the IANA HTTP status code registry.
UnregisteredStatusCode(u16),
}
impl StatusCode {
/// Get the standardised `reason-phrase` for this status code.
///
/// This is mostly here for servers writing responses, but could potentially have application
/// at other times.
///
/// The reason phrase is defined as being exclusively for human readers. You should avoid
/// deriving any meaning from it at all costs.
///
/// Bear in mind also that in HTTP/2.0 the reason phrase is abolished from transmission, and so
/// this canonical reason phrase really is the only reason phrase you’ll find.
pub fn canonical_reason(&self) -> Option<&'static str> {
match *self {
Continue => Some("Continue"),
SwitchingProtocols => Some("Switching Protocols"),
Processing => Some("Processing"),
Ok => Some("OK"),
Created => Some("Created"),
Accepted => Some("Accepted"),
NonAuthoritativeInformation => Some("Non-Authoritative Information"),
NoContent => Some("No Content"),
ResetContent => Some("Reset Content"),
PartialContent => Some("Partial Content"),
MultiStatus => Some("Multi-Status"),
AlreadyReported => Some("Already Reported"),
ImUsed => Some("IM Used"),
MultipleChoices => Some("Multiple Choices"),
MovedPermanently => Some("Moved Permanently"),
Found => Some("Found"),
SeeOther => Some("See Other"),
NotModified => Some("Not Modified"),
UseProxy => Some("Use Proxy"),
TemporaryRedirect => Some("Temporary Redirect"),
PermanentRedirect => Some("Permanent Redirect"),
BadRequest => Some("Bad Request"),
Unauthorized => Some("Unauthorized"),
PaymentRequired => Some("Payment Required"),
Forbidden => Some("Forbidden"),
NotFound => Some("Not Found"),
MethodNotAllowed => Some("Method Not Allowed"),
NotAcceptable => Some("Not Acceptable"),
ProxyAuthenticationRequired => Some("Proxy Authentication Required"),
RequestTimeout => Some("Request Timeout"),
Conflict => Some("Conflict"),
Gone => Some("Gone"),
LengthRequired => Some("Length Required"),
PreconditionFailed => Some("Precondition Failed"),
PayloadTooLarge => Some("Payload Too Large"),
UriTooLong => Some("URI Too Long"),
UnsupportedMediaType => Some("Unsupported Media Type"),
RangeNotSatisfiable => Some("Range Not Satisfiable"),
ExpectationFailed => Some("Expectation Failed"),
ImATeapot => Some("I'm a teapot"),
UnprocessableEntity => Some("Unprocessable Entity"),
Locked => Some("Locked"),
FailedDependency => Some("Failed Dependency"),
UpgradeRequired => Some("Upgrade Required"),
PreconditionRequired => Some("Precondition Required"),
TooManyRequests => Some("Too Many Requests"),
RequestHeaderFieldsTooLarge => Some("Request Header Fields Too Large"),
InternalServerError => Some("Internal Server Error"),
NotImplemented => Some("Not Implemented"),
BadGateway => Some("Bad Gateway"),
ServiceUnavailable => Some("Service Unavailable"),
GatewayTimeout => Some("Gateway Timeout"),
HttpVersionNotSupported => Some("HTTP Version Not Supported"),
VariantAlsoNegotiates => Some("Variant Also Negotiates"),
InsufficientStorage => Some("Insufficient Storage"),
LoopDetected => Some("Loop Detected"),
NotExtended => Some("Not Extended"),
NetworkAuthenticationRequired => Some("Network Authentication Required"),
_ => None
}
}
/// Determine the class of a status code, based on its first digit.
pub fn class(&self) -> StatusClass {
let code = self.to_u16().unwrap();
// TODO rewrite
if code < 100 {
NoClass
} else if code < 200 {
Informational
} else if code < 300 {
Successful
} else if code < 400 {
Redirection
} else if code < 500 {
ClientError
} else if code < 600{
ServerError
} else {
NoClass
}
}
}
impl Copy for StatusCode {}
/// Formats the status code, *including* the canonical reason.
///
/// ```rust
/// # use hyperprotocol::status::{ImATeapot, UnregisteredStatusCode};
/// assert_eq!(format!("{}", ImATeapot).as_slice(),
/// "418 I'm a teapot");
/// assert_eq!(format!("{}", UnregisteredStatusCode(123)).as_slice(),
/// "123 <unknown status code>");
/// ```
///
/// If you wish to just include the number, cast to a `u16` instead:
///
/// ```rust
/// # use std::num::ToPrimitive;
/// # use hyperprotocol::status::{ImATeapot, UnregisteredStatusCode};
/// assert_eq!(format!("{}", ImATeapot.to_u16().unwrap()).as_slice(), "418");
/// assert_eq!(format!("{}", UnregisteredStatusCode(123).to_u16().unwrap()).as_slice(), "123");
/// ```
impl fmt::Display for StatusCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.to_u16().unwrap(),
self.canonical_reason().unwrap_or("<unknown status code>"))
}
}
impl fmt::Debug for StatusCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {
UnregisteredStatusCode(code) => "other FIXME",
Continue => "Continue",
SwitchingProtocols => "SwitchingProtocols",
Processing => "Processing",
Ok => "Ok",
Created => "Created",
Accepted => "Accepted",
NonAuthoritativeInformation => "NonAuthoritativeInformation",
NoContent => "NoContent",
ResetContent => "ResetContent",
PartialContent => "PartialContent",
MultiStatus => "MultiStatus",
AlreadyReported => "AlreadyReported",
ImUsed => "ImUsed",
MultipleChoices => "MultipleChoices",
MovedPermanently => "MovedPermanently",
Found => "Found",
SeeOther => "SeeOther",
NotModified => "NotModified",
UseProxy => "UseProxy",
TemporaryRedirect => "TemporaryRedirect",
PermanentRedirect => "PermanentRedirect",
BadRequest => "BadRequest",
Unauthorized => "Unauthorized",
PaymentRequired => "PaymentRequired",
Forbidden => "Forbidden",
NotFound => "NotFound",
MethodNotAllowed => "MethodNotAllowed",
NotAcceptable => "NotAcceptable",
ProxyAuthenticationRequired => "ProxyAuthenticationRequired",
RequestTimeout => "RequestTimeout",
Conflict => "Conflict",
Gone => "Gone",
LengthRequired => "LengthRequired",
PreconditionFailed => "PreconditionFailed",
PayloadTooLarge => "PayloadTooLarge",
UriTooLong => "UriTooLong",
UnsupportedMediaType => "UnsupportedMediaType",
RangeNotSatisfiable => "RangeNotSatisfiable",
ExpectationFailed => "ExpectationFailed",
ImATeapot => "ImATeapot",
UnprocessableEntity => "UnprocessableEntity",
Locked => "Locked",
FailedDependency => "FailedDependency",
UpgradeRequired => "UpgradeRequired",
PreconditionRequired => "PreconditionRequired",
TooManyRequests => "TooManyRequests",
RequestHeaderFieldsTooLarge => "RequestHeaderFieldsTooLarge",
InternalServerError => "InternalServerError",
NotImplemented => "NotImplemented",
BadGateway => "BadGateway",
ServiceUnavailable => "ServiceUnavailable",
GatewayTimeout => "GatewayTimeout",
HttpVersionNotSupported => "HttpVersionNotSupported",
VariantAlsoNegotiates => "VariantAlsoNegotiates",
InsufficientStorage => "InsufficientStorage",
LoopDetected => "LoopDetected",
NotExtended => "NotExtended",
NetworkAuthenticationRequired => "NetworkAuthenticationRequired",
})
}
}
// Specified manually because the codegen for derived is slow (at the time of writing on the
// machine of writing, 1.2 seconds) and verbose (though the optimiser cuts it down to size).
impl PartialEq for StatusCode {
#[inline]
fn eq(&self, other: &StatusCode) -> bool {
self.to_u16() == other.to_u16()
}
}
impl Eq for StatusCode {}
// Ditto (though #[derive(Clone)] only takes about 0.4 seconds).
impl Clone for StatusCode {
#[inline]
fn clone(&self) -> StatusCode {
*self
}
}
// Of the other common derivable traits, I didn’t measure them, but I guess they would be slow too.
impl FromPrimitive for StatusCode {
fn from_i64(n: i64) -> Option<StatusCode> {
if n < 0 || n > 65535 {
None
} else {
Some(match n {
100 => Continue,
101 => SwitchingProtocols,
102 => Processing,
200 => Ok,
201 => Created,
202 => Accepted,
203 => NonAuthoritativeInformation,
204 => NoContent,
205 => ResetContent,
206 => PartialContent,
207 => MultiStatus,
208 => AlreadyReported,
226 => ImUsed,
300 => MultipleChoices,
301 => MovedPermanently,
302 => Found,
303 => SeeOther,
304 => NotModified,
305 => UseProxy,
307 => TemporaryRedirect,
308 => PermanentRedirect,
400 => BadRequest,
401 => Unauthorized,
402 => PaymentRequired,
403 => Forbidden,
404 => NotFound,
405 => MethodNotAllowed,
406 => NotAcceptable,
407 => ProxyAuthenticationRequired,
408 => RequestTimeout,
409 => Conflict,
410 => Gone,
411 => LengthRequired,
412 => PreconditionFailed,
413 => PayloadTooLarge,
414 => UriTooLong,
415 => UnsupportedMediaType,
416 => RangeNotSatisfiable,
417 => ExpectationFailed,
418 => ImATeapot,
422 => UnprocessableEntity,
423 => Locked,
424 => FailedDependency,
426 => UpgradeRequired,
428 => PreconditionRequired,
429 => TooManyRequests,
431 => RequestHeaderFieldsTooLarge,
500 => InternalServerError,
501 => NotImplemented,
502 => BadGateway,
503 => ServiceUnavailable,
504 => GatewayTimeout,
505 => HttpVersionNotSupported,
506 => VariantAlsoNegotiates,
507 => InsufficientStorage,
508 => LoopDetected,
510 => NotExtended,
511 => NetworkAuthenticationRequired,
_ => UnregisteredStatusCode(n as u16),
})
}
}
fn from_u64(n: u64) -> Option<StatusCode> {
if n > 65535 {
None
} else {
Some(match n {
100 => Continue,
101 => SwitchingProtocols,
102 => Processing,
200 => Ok,
201 => Created,
202 => Accepted,
203 => NonAuthoritativeInformation,
204 => NoContent,
205 => ResetContent,
206 => PartialContent,
207 => MultiStatus,
208 => AlreadyReported,
226 => ImUsed,
300 => MultipleChoices,
301 => MovedPermanently,
302 => Found,
303 => SeeOther,
304 => NotModified,
305 => UseProxy,
307 => TemporaryRedirect,
308 => PermanentRedirect,
400 => BadRequest,
401 => Unauthorized,
402 => PaymentRequired,
403 => Forbidden,
404 => NotFound,
405 => MethodNotAllowed,
406 => NotAcceptable,
407 => ProxyAuthenticationRequired,
408 => RequestTimeout,
409 => Conflict,
410 => Gone,
411 => LengthRequired,
412 => PreconditionFailed,
413 => PayloadTooLarge,
414 => UriTooLong,
415 => UnsupportedMediaType,
416 => RangeNotSatisfiable,
417 => ExpectationFailed,
418 => ImATeapot,
422 => UnprocessableEntity,
423 => Locked,
424 => FailedDependency,
426 => UpgradeRequired,
428 => PreconditionRequired,
429 => TooManyRequests,
431 => RequestHeaderFieldsTooLarge,
500 => InternalServerError,
501 => NotImplemented,
502 => BadGateway,
503 => ServiceUnavailable,
504 => GatewayTimeout,
505 => HttpVersionNotSupported,
506 => VariantAlsoNegotiates,
507 => InsufficientStorage,
508 => LoopDetected,
510 => NotExtended,
511 => NetworkAuthenticationRequired,
_ => UnregisteredStatusCode(n as u16),
})
}
}
}
impl PartialOrd for StatusCode {
#[inline]
fn partial_cmp(&self, other: &StatusCode) -> Option<Ordering> {
self.to_u16().unwrap().partial_cmp(&(other.to_u16().unwrap()))
}
}
impl Ord for StatusCode {
#[inline]
fn cmp(&self, other: &StatusCode) -> Ordering {
if *self < *other {
Ordering::Less
} else if *self > *other {
Ordering::Greater
} else {
Ordering::Equal
}
}
}
impl ToPrimitive for StatusCode {
fn to_i64(&self) -> Option<i64> {
Some(match *self {
Continue => 100,
SwitchingProtocols => 101,
Processing => 102,
Ok => 200,
Created => 201,
Accepted => 202,
NonAuthoritativeInformation => 203,
NoContent => 204,
ResetContent => 205,
PartialContent => 206,
MultiStatus => 207,
AlreadyReported => 208,
ImUsed => 226,
MultipleChoices => 300,
MovedPermanently => 301,
Found => 302,
SeeOther => 303,
NotModified => 304,
UseProxy => 305,
TemporaryRedirect => 307,
PermanentRedirect => 308,
BadRequest => 400,
Unauthorized => 401,
PaymentRequired => 402,
Forbidden => 403,
NotFound => 404,
MethodNotAllowed => 405,
NotAcceptable => 406,
ProxyAuthenticationRequired => 407,
RequestTimeout => 408,
Conflict => 409,
Gone => 410,
LengthRequired => 411,
PreconditionFailed => 412,
PayloadTooLarge => 413,
UriTooLong => 414,
UnsupportedMediaType => 415,
RangeNotSatisfiable => 416,
ExpectationFailed => 417,
ImATeapot => 418,
UnprocessableEntity => 422,
Locked => 423,
FailedDependency => 424,
UpgradeRequired => 426,
PreconditionRequired => 428,
TooManyRequests => 429,
RequestHeaderFieldsTooLarge => 431,
InternalServerError => 500,
NotImplemented => 501,
BadGateway => 502,
ServiceUnavailable => 503,
GatewayTimeout => 504,
HttpVersionNotSupported => 505,
VariantAlsoNegotiates => 506,
InsufficientStorage => 507,
LoopDetected => 508,
NotExtended => 510,
NetworkAuthenticationRequired => 511,
UnregisteredStatusCode(n) => n,
} as i64)
}
fn to_u64(&self) -> Option<u64> {
Some(match *self {
Continue => 100,
SwitchingProtocols => 101,
Processing => 102,
Ok => 200,
Created => 201,
Accepted => 202,
NonAuthoritativeInformation => 203,
NoContent => 204,
ResetContent => 205,
PartialContent => 206,
MultiStatus => 207,
AlreadyReported => 208,
ImUsed => 226,
MultipleChoices => 300,
MovedPermanently => 301,
Found => 302,
SeeOther => 303,
NotModified => 304,
UseProxy => 305,
TemporaryRedirect => 307,
PermanentRedirect => 308,
BadRequest => 400,
Unauthorized => 401,
PaymentRequired => 402,
Forbidden => 403,
NotFound => 404,
MethodNotAllowed => 405,
NotAcceptable => 406,
ProxyAuthenticationRequired => 407,
RequestTimeout => 408,
Conflict => 409,
Gone => 410,
LengthRequired => 411,
PreconditionFailed => 412,
PayloadTooLarge => 413,
UriTooLong => 414,
UnsupportedMediaType => 415,
RangeNotSatisfiable => 416,
ExpectationFailed => 417,
ImATeapot => 418,
UnprocessableEntity => 422,
Locked => 423,
FailedDependency => 424,
UpgradeRequired => 426,
PreconditionRequired => 428,
TooManyRequests => 429,
RequestHeaderFieldsTooLarge => 431,
InternalServerError => 500,
NotImplemented => 501,
BadGateway => 502,
ServiceUnavailable => 503,
GatewayTimeout => 504,
HttpVersionNotSupported => 505,
VariantAlsoNegotiates => 506,
InsufficientStorage => 507,
LoopDetected => 508,
NotExtended => 510,
NetworkAuthenticationRequired => 511,
UnregisteredStatusCode(n) => n,
} as u64)
}
}
/// The class of an HTTP `status-code`.
///
/// [RFC 7231, section 6 (Response Status Codes)](https://tools.ietf.org/html/rfc7231#section-6):
///
/// > The first digit of the status-code defines the class of response.
/// > The last two digits do not have any categorization role.
///
/// And:
///
/// > HTTP status codes are extensible. HTTP clients are not required to
/// > understand the meaning of all registered status codes, though such
/// > understanding is obviously desirable. However, a client MUST
/// > understand the class of any status code, as indicated by the first
/// > digit, and treat an unrecognized status code as being equivalent to
/// > the x00 status code of that class, with the exception that a
/// > recipient MUST NOT cache a response with an unrecognized status code.
/// >
/// > For example, if an unrecognized status code of 471 is received by a
/// > client, the client can assume that there was something wrong with its
/// > request and treat the response as if it had received a 400 (Bad
/// > Request) status code. The response message will usually contain a
/// > representation that explains the status.
///
/// This can be used in cases where a status code’s meaning is unknown, also,
/// to get the appropriate *category* of status.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Copy)]
pub enum StatusClass {
/// 1xx (Informational): The request was received, continuing process
Informational,
/// 2xx (Successful): The request was successfully received, understood, and accepted
Successful,
/// 3xx (Redirection): Further action needs to be taken in order to complete the request
Redirection,
/// 4xx (Client Error): The request contains bad syntax or cannot be fulfilled
ClientError,
/// 5xx (Server Error): The server failed to fulfill an apparently valid request
ServerError,
/// Some strange status code
NoClass,
}
impl StatusClass {
/// Get the default status code for the class.
///
/// This produces the x00 status code; thus, for `ClientError` (4xx), for example, this will
/// produce `BadRequest` (400):
///
/// ```rust
/// # use hyperprotocol::status::{ClientError, BadRequest};
/// assert_eq!(ClientError.default_code(), BadRequest);
/// ```
///
/// The use for this is outlined in [RFC 7231, section 6 (Response Status
/// Codes)](https://tools.ietf.org/html/rfc7231#section-6):
///
/// > HTTP status codes are extensible. HTTP clients are not required to
/// > understand the meaning of all registered status codes, though such
/// > understanding is obviously desirable. However, a client MUST
/// > understand the class of any status code, as indicated by the first
/// > digit, and treat an unrecognized status code as being equivalent to
/// > the x00 status code of that class, with the exception that a
/// > recipient MUST NOT cache a response with an unrecognized status code.
/// >
/// > For example, if an unrecognized status code of 471 is received by a
/// > client, the client can assume that there was something wrong with its
/// > request and treat the response as if it had received a 400 (Bad
/// > Request) status code. The response message will usually contain a
/// > representation that explains the status.
///
/// This is demonstrated thusly:
///
/// ```rust
/// # use hyperprotocol::status::{UnregisteredStatusCode, BadRequest};
/// // Suppose we have received this status code.
/// let status = UnregisteredStatusCode(471);
///
/// // Uh oh! Don’t know what to do with it.
/// // Let’s fall back to the default:
/// let status = status.class().default_code();
///
/// // And look! That is 400 Bad Request.
/// assert_eq!(status, BadRequest);
/// // So now let’s treat it as that.
/// ```
pub fn default_code(&self) -> StatusCode {
match *self {
Informational => Continue,
Successful => Ok,
Redirection => MultipleChoices,
ClientError => BadRequest,
ServerError => InternalServerError,
NoClass => Ok,
}
}
}
impl ToPrimitive for StatusClass {
fn to_i64(&self) -> Option<i64> {
Some(match *self {
Informational => 100,
Successful => 200,
Redirection => 300,
ClientError => 400,
ServerError => 500,
NoClass => 200,
})
}
fn to_u64(&self) -> Option<u64> {
Some(match *self {
Informational => 100,
Successful => 200,
Redirection => 300,
ClientError => 400,
ServerError => 500,
NoClass => 200,
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment