use std::fmt;
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum StatusClass {
Informational,
Success,
Redirection,
ClientError,
ServerError,
Unknown
}
macro_rules! class_check_fn {
($func:ident, $type:expr, $variant:ident) => (
#[doc=$type]
#[inline(always)]
pub fn $func(&self) -> bool {
*self == StatusClass::$variant
}
)
}
impl StatusClass {
class_check_fn!(is_informational, "`Informational` (1XX).", Informational);
class_check_fn!(is_success, "`Success` (2XX).", Success);
class_check_fn!(is_redirection, "`Redirection` (3XX).", Redirection);
class_check_fn!(is_client_error, "`ClientError` (4XX).", ClientError);
class_check_fn!(is_server_error, "`ServerError` (5XX).", ServerError);
class_check_fn!(is_unknown, "`Unknown`.", Unknown);
}
#[derive(Debug, Clone, Copy)]
pub struct Status {
pub code: u16,
}
impl Default for Status {
fn default() -> Self {
Status::Ok
}
}
macro_rules! ctrs {
($($code:expr, $code_str:expr, $name:ident => $reason:expr),+) => {
$(
#[doc="[`Status`] with code <b>"]
#[doc=$code_str]
#[doc="</b>."]
#[allow(non_upper_case_globals)]
pub const $name: Status = Status { code: $code };
)+
pub const fn new(code: u16) -> Status {
Status { code }
}
pub const fn class(self) -> StatusClass {
match self.code / 100 {
1 => StatusClass::Informational,
2 => StatusClass::Success,
3 => StatusClass::Redirection,
4 => StatusClass::ClientError,
5 => StatusClass::ServerError,
_ => StatusClass::Unknown
}
}
pub const fn from_code(code: u16) -> Option<Status> {
match code {
$($code => Some(Status::$name),)+
_ => None
}
}
pub const fn reason(&self) -> Option<&'static str> {
match self.code {
$($code => Some($reason),)+
_ => None
}
}
pub const fn reason_lossy(&self) -> &'static str {
if let Some(lossless) = self.reason() {
return lossless;
}
match self.class() {
StatusClass::Informational => "Informational",
StatusClass::Success => "Success",
StatusClass::Redirection => "Redirection",
StatusClass::ClientError => "Client Error",
StatusClass::ServerError => "Server Error",
StatusClass::Unknown => "Unknown"
}
}
};
}
impl Status {
ctrs! {
100, "100", Continue => "Continue",
101, "101", SwitchingProtocols => "Switching Protocols",
102, "102", Processing => "Processing",
200, "200", Ok => "OK",
201, "201", Created => "Created",
202, "202", Accepted => "Accepted",
203, "203", NonAuthoritativeInformation => "Non-Authoritative Information",
204, "204", NoContent => "No Content",
205, "205", ResetContent => "Reset Content",
206, "206", PartialContent => "Partial Content",
207, "207", MultiStatus => "Multi-Status",
208, "208", AlreadyReported => "Already Reported",
226, "226", ImUsed => "IM Used",
300, "300", MultipleChoices => "Multiple Choices",
301, "301", MovedPermanently => "Moved Permanently",
302, "302", Found => "Found",
303, "303", SeeOther => "See Other",
304, "304", NotModified => "Not Modified",
305, "305", UseProxy => "Use Proxy",
307, "307", TemporaryRedirect => "Temporary Redirect",
308, "308", PermanentRedirect => "Permanent Redirect",
400, "400", BadRequest => "Bad Request",
401, "401", Unauthorized => "Unauthorized",
402, "402", PaymentRequired => "Payment Required",
403, "403", Forbidden => "Forbidden",
404, "404", NotFound => "Not Found",
405, "405", MethodNotAllowed => "Method Not Allowed",
406, "406", NotAcceptable => "Not Acceptable",
407, "407", ProxyAuthenticationRequired => "Proxy Authentication Required",
408, "408", RequestTimeout => "Request Timeout",
409, "409", Conflict => "Conflict",
410, "410", Gone => "Gone",
411, "411", LengthRequired => "Length Required",
412, "412", PreconditionFailed => "Precondition Failed",
413, "413", PayloadTooLarge => "Payload Too Large",
414, "414", UriTooLong => "URI Too Long",
415, "415", UnsupportedMediaType => "Unsupported Media Type",
416, "416", RangeNotSatisfiable => "Range Not Satisfiable",
417, "417", ExpectationFailed => "Expectation Failed",
418, "418", ImATeapot => "I'm a teapot",
421, "421", MisdirectedRequest => "Misdirected Request",
422, "422", UnprocessableEntity => "Unprocessable Entity",
423, "423", Locked => "Locked",
424, "424", FailedDependency => "Failed Dependency",
426, "426", UpgradeRequired => "Upgrade Required",
428, "428", PreconditionRequired => "Precondition Required",
429, "429", TooManyRequests => "Too Many Requests",
431, "431", RequestHeaderFieldsTooLarge => "Request Header Fields Too Large",
451, "451", UnavailableForLegalReasons => "Unavailable For Legal Reasons",
500, "500", InternalServerError => "Internal Server Error",
501, "501", NotImplemented => "Not Implemented",
502, "502", BadGateway => "Bad Gateway",
503, "503", ServiceUnavailable => "Service Unavailable",
504, "504", GatewayTimeout => "Gateway Timeout",
505, "505", HttpVersionNotSupported => "HTTP Version Not Supported",
506, "506", VariantAlsoNegotiates => "Variant Also Negotiates",
507, "507", InsufficientStorage => "Insufficient Storage",
508, "508", LoopDetected => "Loop Detected",
510, "510", NotExtended => "Not Extended",
511, "511", NetworkAuthenticationRequired => "Network Authentication Required"
}
}
impl fmt::Display for Status {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {}", self.code, self.reason_lossy())
}
}
impl std::hash::Hash for Status {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.code.hash(state)
}
}
impl PartialEq for Status {
fn eq(&self, other: &Self) -> bool {
self.code.eq(&other.code)
}
}
impl Eq for Status { }
impl PartialOrd for Status {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.code.partial_cmp(&other.code)
}
}
impl Ord for Status {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.code.cmp(&other.code)
}
}
#[cfg(feature = "serde")]
mod serde {
use std::fmt;
use super::*;
use serde_::ser::{Serialize, Serializer};
use serde_::de::{Deserialize, Deserializer, Error, Visitor, Unexpected};
impl<'a> Serialize for Status {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_u16(self.code)
}
}
struct DeVisitor;
impl<'de> Visitor<'de> for DeVisitor {
type Value = Status;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "HTTP status code integer in range [100, 600)")
}
fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> {
if v < 100 || v >= 600 {
return Err(E::invalid_value(Unexpected::Signed(v), &self));
}
Ok(Status::new(v as u16))
}
fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
if v < 100 || v >= 600 {
return Err(E::invalid_value(Unexpected::Unsigned(v), &self));
}
Ok(Status::new(v as u16))
}
}
impl<'de> Deserialize<'de> for Status {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_u16(DeVisitor)
}
}
}