use crate::util::three_digit_to_utf;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum StatusCode {
Continue,
SwitchingProtocols,
OK,
Created,
Accepted,
NonAuthoritative,
NoContent,
ResetContent,
PartialContent,
MultipleChoices,
MovedPermanently,
Found,
SeeOther,
NotModified,
UseProxy,
TemporaryRedirect,
PermanentRedirect,
BadRequest,
Unauthorized,
PaymentRequired,
Forbidden,
NotFound,
MethodNotAllowed,
NotAcceptable,
ProxyAuthenticationRequired,
RequestTimeout,
Conflict,
Gone,
LengthRequired,
PreconditionFailed,
#[deprecated]
RequestEntityTooLarge,
ContentTooLarge,
RequestURITooLong,
UnsupportedMediaType,
RequestedRangeNotSatisfiable,
ExpectationFailed,
InternalServerError,
NotImplemented,
BadGateway,
ServiceUnavailable,
GatewayTimeout,
VersionNotSupported,
CustomStr(u16, [u8; 3], &'static str),
CustomString(u16, [u8; 3], String),
}
impl StatusCode {
pub const fn from_custom(code: u16, status_line: &'static str) -> Self {
if !status_line.is_ascii() || status_line.is_empty() {
return Self::InternalServerError;
}
if code < 100 || code > 999 {
return Self::InternalServerError;
}
Self::CustomStr(code, three_digit_to_utf(code), status_line)
}
pub fn from_custom_string<T: ToString>(code: u16, status_line: &T) -> Option<Self> {
let status_line = status_line.to_string();
if !status_line.is_ascii() || status_line.is_empty() {
return None;
}
if !(100..=999).contains(&code) {
return None;
}
if let Some(well_known) = Self::from_well_known_code(code) {
if well_known.status_line() == status_line.as_str() {
return Some(well_known);
}
}
Some(Self::CustomString(code, three_digit_to_utf(code), status_line))
}
pub const fn from_well_known_code_or_500(code: u16) -> Self {
match code {
100 => StatusCode::Continue,
101 => StatusCode::SwitchingProtocols,
200 => StatusCode::OK,
201 => StatusCode::Created,
202 => StatusCode::Accepted,
203 => StatusCode::NonAuthoritative,
204 => StatusCode::NoContent,
205 => StatusCode::ResetContent,
206 => StatusCode::PartialContent,
300 => StatusCode::MultipleChoices,
301 => StatusCode::MovedPermanently,
302 => StatusCode::Found,
303 => StatusCode::SeeOther,
304 => StatusCode::NotModified,
305 => StatusCode::UseProxy,
307 => StatusCode::TemporaryRedirect,
400 => StatusCode::BadRequest,
401 => StatusCode::Unauthorized,
403 => StatusCode::Forbidden,
404 => StatusCode::NotFound,
405 => StatusCode::MethodNotAllowed,
406 => StatusCode::NotAcceptable,
407 => StatusCode::ProxyAuthenticationRequired,
408 => StatusCode::RequestTimeout,
409 => StatusCode::Conflict,
410 => StatusCode::Gone,
411 => StatusCode::LengthRequired,
412 => StatusCode::PreconditionFailed,
413 => StatusCode::ContentTooLarge,
414 => StatusCode::RequestURITooLong,
415 => StatusCode::UnsupportedMediaType,
416 => StatusCode::RequestedRangeNotSatisfiable,
417 => StatusCode::ExpectationFailed,
501 => StatusCode::NotImplemented,
502 => StatusCode::BadGateway,
503 => StatusCode::ServiceUnavailable,
504 => StatusCode::GatewayTimeout,
505 => StatusCode::VersionNotSupported,
_ => StatusCode::InternalServerError,
}
}
pub const fn from_well_known_code(code: u16) -> Option<Self> {
Some(match code {
100 => StatusCode::Continue,
101 => StatusCode::SwitchingProtocols,
200 => StatusCode::OK,
201 => StatusCode::Created,
202 => StatusCode::Accepted,
203 => StatusCode::NonAuthoritative,
204 => StatusCode::NoContent,
205 => StatusCode::ResetContent,
206 => StatusCode::PartialContent,
300 => StatusCode::MultipleChoices,
301 => StatusCode::MovedPermanently,
302 => StatusCode::Found,
303 => StatusCode::SeeOther,
304 => StatusCode::NotModified,
305 => StatusCode::UseProxy,
307 => StatusCode::TemporaryRedirect,
400 => StatusCode::BadRequest,
401 => StatusCode::Unauthorized,
403 => StatusCode::Forbidden,
404 => StatusCode::NotFound,
405 => StatusCode::MethodNotAllowed,
406 => StatusCode::NotAcceptable,
407 => StatusCode::ProxyAuthenticationRequired,
408 => StatusCode::RequestTimeout,
409 => StatusCode::Conflict,
410 => StatusCode::Gone,
411 => StatusCode::LengthRequired,
412 => StatusCode::PreconditionFailed,
413 => StatusCode::ContentTooLarge,
414 => StatusCode::RequestURITooLong,
415 => StatusCode::UnsupportedMediaType,
416 => StatusCode::RequestedRangeNotSatisfiable,
417 => StatusCode::ExpectationFailed,
500 => StatusCode::InternalServerError,
501 => StatusCode::NotImplemented,
502 => StatusCode::BadGateway,
503 => StatusCode::ServiceUnavailable,
504 => StatusCode::GatewayTimeout,
505 => StatusCode::VersionNotSupported,
_ => return None,
})
}
pub const fn status_line_static(&self) -> Option<&'static str> {
Some(match self {
StatusCode::Continue => "Continue",
StatusCode::SwitchingProtocols => "Switching Protocols",
StatusCode::OK => "OK",
StatusCode::Created => "Created",
StatusCode::Accepted => "Accepted",
StatusCode::NonAuthoritative => "Non-Authoritative Information",
StatusCode::NoContent => "No Content",
StatusCode::ResetContent => "Reset Content",
StatusCode::PartialContent => "Partial Content",
StatusCode::MultipleChoices => "Multiple Choices",
StatusCode::MovedPermanently => "Moved Permanently",
StatusCode::Found => "Found",
StatusCode::SeeOther => "See Other",
StatusCode::NotModified => "Not Modified",
StatusCode::UseProxy => "Use Proxy",
StatusCode::TemporaryRedirect => "Temporary Redirect",
StatusCode::BadRequest => "Bad Request",
StatusCode::Unauthorized => "Unauthorized",
StatusCode::Forbidden => "Forbidden",
StatusCode::NotFound => "Not Found",
StatusCode::MethodNotAllowed => "Method Not Allowed",
StatusCode::NotAcceptable => "Not Acceptable",
StatusCode::ProxyAuthenticationRequired => "Proxy Authentication Required",
StatusCode::RequestTimeout => "Request Timeout",
StatusCode::Conflict => "Conflict",
StatusCode::Gone => "Gone",
StatusCode::LengthRequired => "Length Required",
StatusCode::PreconditionFailed => "Precondition Failed",
#[expect(deprecated)]
StatusCode::RequestEntityTooLarge => "Request Entity Too Large",
StatusCode::ContentTooLarge => "Content Too Large",
StatusCode::RequestURITooLong => "Request-URI Too Long",
StatusCode::UnsupportedMediaType => "Unsupported Media Type",
StatusCode::RequestedRangeNotSatisfiable => "Requested Range Not Satisfiable",
StatusCode::ExpectationFailed => "Expectation Failed",
StatusCode::InternalServerError => "Internal Server Error",
StatusCode::NotImplemented => "Not Implemented",
StatusCode::BadGateway => "Bad Gateway",
StatusCode::ServiceUnavailable => "Service Unavailable",
StatusCode::GatewayTimeout => "Gateway Timeout",
StatusCode::VersionNotSupported => "HTTP Version Not Supported",
StatusCode::PermanentRedirect => "Permanent Redirect",
StatusCode::PaymentRequired => "Payment Required",
StatusCode::CustomStr(_, _, str) => str,
StatusCode::CustomString(_, _, _) => return None,
})
}
pub fn status_line(&self) -> &str {
match self {
StatusCode::Continue => "Continue",
StatusCode::SwitchingProtocols => "Switching Protocols",
StatusCode::OK => "OK",
StatusCode::Created => "Created",
StatusCode::Accepted => "Accepted",
StatusCode::NonAuthoritative => "Non-Authoritative Information",
StatusCode::NoContent => "No Content",
StatusCode::ResetContent => "Reset Content",
StatusCode::PartialContent => "Partial Content",
StatusCode::MultipleChoices => "Multiple Choices",
StatusCode::MovedPermanently => "Moved Permanently",
StatusCode::Found => "Found",
StatusCode::SeeOther => "See Other",
StatusCode::NotModified => "Not Modified",
StatusCode::UseProxy => "Use Proxy",
StatusCode::TemporaryRedirect => "Temporary Redirect",
StatusCode::BadRequest => "Bad Request",
StatusCode::Unauthorized => "Unauthorized",
StatusCode::Forbidden => "Forbidden",
StatusCode::NotFound => "Not Found",
StatusCode::MethodNotAllowed => "Method Not Allowed",
StatusCode::NotAcceptable => "Not Acceptable",
StatusCode::ProxyAuthenticationRequired => "Proxy Authentication Required",
StatusCode::RequestTimeout => "Request Timeout",
StatusCode::Conflict => "Conflict",
StatusCode::Gone => "Gone",
StatusCode::LengthRequired => "Length Required",
StatusCode::PreconditionFailed => "Precondition Failed",
#[expect(deprecated)]
StatusCode::RequestEntityTooLarge => "Request Entity Too Large",
StatusCode::ContentTooLarge => "Content Too Large",
StatusCode::RequestURITooLong => "Request-URI Too Long",
StatusCode::UnsupportedMediaType => "Unsupported Media Type",
StatusCode::RequestedRangeNotSatisfiable => "Requested Range Not Satisfiable",
StatusCode::ExpectationFailed => "Expectation Failed",
StatusCode::InternalServerError => "Internal Server Error",
StatusCode::NotImplemented => "Not Implemented",
StatusCode::BadGateway => "Bad Gateway",
StatusCode::ServiceUnavailable => "Service Unavailable",
StatusCode::GatewayTimeout => "Gateway Timeout",
StatusCode::VersionNotSupported => "HTTP Version Not Supported",
StatusCode::PermanentRedirect => "Permanent Redirect",
StatusCode::PaymentRequired => "Payment Required",
StatusCode::CustomStr(_, _, str) => str,
StatusCode::CustomString(_, _, str) => str.as_str(),
}
}
pub const fn code_as_utf(&self) -> &[u8; 3] {
match self {
StatusCode::Continue => b"100",
StatusCode::SwitchingProtocols => b"101",
StatusCode::OK => b"200",
StatusCode::Created => b"201",
StatusCode::Accepted => b"202",
StatusCode::NonAuthoritative => b"203",
StatusCode::NoContent => b"204",
StatusCode::ResetContent => b"205",
StatusCode::PartialContent => b"206",
StatusCode::MultipleChoices => b"300",
StatusCode::MovedPermanently => b"301",
StatusCode::Found => b"302",
StatusCode::SeeOther => b"303",
StatusCode::NotModified => b"304",
StatusCode::UseProxy => b"305",
StatusCode::TemporaryRedirect => b"307",
StatusCode::PermanentRedirect => b"308",
StatusCode::BadRequest => b"400",
StatusCode::PaymentRequired => b"402",
StatusCode::Unauthorized => b"401",
StatusCode::Forbidden => b"403",
StatusCode::NotFound => b"404",
StatusCode::MethodNotAllowed => b"405",
StatusCode::NotAcceptable => b"406",
StatusCode::ProxyAuthenticationRequired => b"407",
StatusCode::RequestTimeout => b"408",
StatusCode::Conflict => b"409",
StatusCode::Gone => b"410",
StatusCode::LengthRequired => b"411",
StatusCode::PreconditionFailed => b"412",
#[expect(deprecated)]
StatusCode::RequestEntityTooLarge => b"413",
StatusCode::ContentTooLarge => b"413",
StatusCode::RequestURITooLong => b"414",
StatusCode::UnsupportedMediaType => b"415",
StatusCode::RequestedRangeNotSatisfiable => b"416",
StatusCode::ExpectationFailed => b"417",
StatusCode::InternalServerError => b"500",
StatusCode::NotImplemented => b"501",
StatusCode::BadGateway => b"502",
StatusCode::ServiceUnavailable => b"503",
StatusCode::GatewayTimeout => b"504",
StatusCode::VersionNotSupported => b"505",
StatusCode::CustomStr(_, code, _) => code,
StatusCode::CustomString(_, code, _) => code,
}
}
pub const fn code(&self) -> u16 {
match self {
StatusCode::Continue => 100,
StatusCode::SwitchingProtocols => 101,
StatusCode::OK => 200,
StatusCode::Created => 201,
StatusCode::Accepted => 202,
StatusCode::NonAuthoritative => 203,
StatusCode::NoContent => 204,
StatusCode::ResetContent => 205,
StatusCode::PartialContent => 206,
StatusCode::MultipleChoices => 300,
StatusCode::MovedPermanently => 301,
StatusCode::Found => 302,
StatusCode::SeeOther => 303,
StatusCode::NotModified => 304,
StatusCode::UseProxy => 305,
StatusCode::TemporaryRedirect => 307,
StatusCode::PermanentRedirect => 308,
StatusCode::BadRequest => 400,
StatusCode::Unauthorized => 401,
StatusCode::PaymentRequired => 402,
StatusCode::Forbidden => 403,
StatusCode::NotFound => 404,
StatusCode::MethodNotAllowed => 405,
StatusCode::NotAcceptable => 406,
StatusCode::ProxyAuthenticationRequired => 407,
StatusCode::RequestTimeout => 408,
StatusCode::Conflict => 409,
StatusCode::Gone => 410,
StatusCode::LengthRequired => 411,
StatusCode::PreconditionFailed => 412,
#[expect(deprecated)]
StatusCode::RequestEntityTooLarge => 413,
StatusCode::ContentTooLarge => 413,
StatusCode::RequestURITooLong => 414,
StatusCode::UnsupportedMediaType => 415,
StatusCode::RequestedRangeNotSatisfiable => 416,
StatusCode::ExpectationFailed => 417,
StatusCode::InternalServerError => 500,
StatusCode::NotImplemented => 501,
StatusCode::BadGateway => 502,
StatusCode::ServiceUnavailable => 503,
StatusCode::GatewayTimeout => 504,
StatusCode::VersionNotSupported => 505,
StatusCode::CustomStr(code, _, _) => *code,
StatusCode::CustomString(code, _, _) => *code,
}
}
}