use std::fmt;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ErrorStatusCode(http::StatusCode);
macro_rules! error_status_code_constants {
( $($(#[$docs:meta])* $name:ident;)+ ) => {
$(
$(#[$docs])*
pub const $name: Self = Self(http::StatusCode::$name);
)+
}
}
macro_rules! impl_status_code_wrapper {
(impl StatusCode for $T:ident {
type NotAnError = $NotAnError:ident;
type Invalid = $Invalid:ident;
}) => {
impl $T {
pub fn from_bytes(src: &[u8]) -> Result<Self, $Invalid> {
let status = http::StatusCode::from_bytes(src)?;
Self::from_status(status).map_err(Into::into)
}
pub fn as_status(&self) -> http::StatusCode {
self.0
}
pub fn as_u16(&self) -> u16 {
self.0.as_u16()
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
pub fn canonical_reason(&self) -> Option<&'static str> {
self.0.canonical_reason()
}
}
impl fmt::Debug for $T {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl fmt::Display for $T {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl PartialEq<u16> for $T {
#[inline]
fn eq(&self, other: &u16) -> bool {
self.as_u16() == *other
}
}
impl PartialEq<$T> for u16 {
#[inline]
fn eq(&self, other: &$T) -> bool {
*self == other.as_u16()
}
}
impl PartialEq<http::StatusCode> for $T {
#[inline]
fn eq(&self, other: &http::StatusCode) -> bool {
self.0 == *other
}
}
impl PartialEq<$T> for http::StatusCode {
#[inline]
fn eq(&self, other: &$T) -> bool {
*self == other.0
}
}
impl From<$T> for u16 {
#[inline]
fn from(status: $T) -> u16 {
status.as_u16()
}
}
impl std::str::FromStr for $T {
type Err = $Invalid;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(http::StatusCode::from_str(s)?.try_into()?)
}
}
impl From<&'_ $T> for $T {
#[inline]
fn from(t: &$T) -> Self {
t.to_owned()
}
}
impl TryFrom<&'_ [u8]> for $T {
type Error = $Invalid;
#[inline]
fn try_from(t: &[u8]) -> Result<Self, Self::Error> {
$T::from_bytes(t)
}
}
impl TryFrom<&'_ str> for $T {
type Error = $Invalid;
#[inline]
fn try_from(t: &str) -> Result<Self, Self::Error> {
t.parse()
}
}
impl TryFrom<u16> for $T {
type Error = $Invalid;
#[inline]
fn try_from(t: u16) -> Result<Self, Self::Error> {
Self::from_u16(t)
}
}
impl TryFrom<http::StatusCode> for $T {
type Error = $NotAnError;
fn try_from(value: http::StatusCode) -> Result<Self, Self::Error> {
Self::from_status(value)
}
}
};
}
impl ErrorStatusCode {
error_status_code_constants! {
BAD_REQUEST;
UNAUTHORIZED;
PAYMENT_REQUIRED;
FORBIDDEN;
NOT_FOUND;
METHOD_NOT_ALLOWED;
NOT_ACCEPTABLE;
PROXY_AUTHENTICATION_REQUIRED;
REQUEST_TIMEOUT;
CONFLICT;
GONE;
LENGTH_REQUIRED;
PRECONDITION_FAILED;
PAYLOAD_TOO_LARGE;
URI_TOO_LONG;
UNSUPPORTED_MEDIA_TYPE;
RANGE_NOT_SATISFIABLE;
EXPECTATION_FAILED;
IM_A_TEAPOT;
MISDIRECTED_REQUEST;
UNPROCESSABLE_ENTITY;
LOCKED;
FAILED_DEPENDENCY;
UPGRADE_REQUIRED;
PRECONDITION_REQUIRED;
TOO_MANY_REQUESTS;
REQUEST_HEADER_FIELDS_TOO_LARGE;
UNAVAILABLE_FOR_LEGAL_REASONS;
INTERNAL_SERVER_ERROR;
NOT_IMPLEMENTED;
BAD_GATEWAY;
SERVICE_UNAVAILABLE;
GATEWAY_TIMEOUT;
HTTP_VERSION_NOT_SUPPORTED;
VARIANT_ALSO_NEGOTIATES;
INSUFFICIENT_STORAGE;
LOOP_DETECTED;
NOT_EXTENDED;
NETWORK_AUTHENTICATION_REQUIRED;
}
pub fn from_status(status: http::StatusCode) -> Result<Self, NotAnError> {
if status.is_client_error() || status.is_server_error() {
Ok(ErrorStatusCode(status))
} else {
Err(NotAnError(status))
}
}
pub fn from_u16(src: u16) -> Result<Self, InvalidErrorStatusCode> {
let status = http::StatusCode::from_u16(src)?;
Self::from_status(status).map_err(Into::into)
}
pub fn as_client_error(
&self,
) -> Result<ClientErrorStatusCode, NotAClientError> {
if self.is_client_error() {
Ok(ClientErrorStatusCode(self.0))
} else {
Err(NotAClientError(self.0))
}
}
pub fn is_client_error(&self) -> bool {
self.0.is_client_error()
}
pub fn is_server_error(&self) -> bool {
self.0.is_server_error()
}
}
impl_status_code_wrapper! {
impl StatusCode for ErrorStatusCode {
type NotAnError = NotAnError;
type Invalid = InvalidErrorStatusCode;
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ClientErrorStatusCode(http::StatusCode);
impl ClientErrorStatusCode {
error_status_code_constants! {
BAD_REQUEST;
UNAUTHORIZED;
PAYMENT_REQUIRED;
FORBIDDEN;
NOT_FOUND;
METHOD_NOT_ALLOWED;
NOT_ACCEPTABLE;
PROXY_AUTHENTICATION_REQUIRED;
REQUEST_TIMEOUT;
CONFLICT;
GONE;
LENGTH_REQUIRED;
PRECONDITION_FAILED;
PAYLOAD_TOO_LARGE;
URI_TOO_LONG;
UNSUPPORTED_MEDIA_TYPE;
RANGE_NOT_SATISFIABLE;
EXPECTATION_FAILED;
IM_A_TEAPOT;
MISDIRECTED_REQUEST;
UNPROCESSABLE_ENTITY;
LOCKED;
FAILED_DEPENDENCY;
UPGRADE_REQUIRED;
PRECONDITION_REQUIRED;
TOO_MANY_REQUESTS;
REQUEST_HEADER_FIELDS_TOO_LARGE;
UNAVAILABLE_FOR_LEGAL_REASONS;
}
pub fn from_status(
status: http::StatusCode,
) -> Result<Self, NotAClientError> {
if status.is_client_error() {
Ok(Self(status))
} else {
Err(NotAClientError(status))
}
}
#[inline]
pub fn from_u16(src: u16) -> Result<Self, InvalidClientErrorStatusCode> {
let status = http::StatusCode::from_u16(src)?;
Self::from_status(status).map_err(Into::into)
}
}
impl_status_code_wrapper! {
impl StatusCode for ClientErrorStatusCode {
type NotAnError = NotAClientError;
type Invalid = InvalidClientErrorStatusCode;
}
}
impl TryFrom<ErrorStatusCode> for ClientErrorStatusCode {
type Error = NotAClientError;
fn try_from(error: ErrorStatusCode) -> Result<Self, Self::Error> {
error.as_client_error()
}
}
impl From<ClientErrorStatusCode> for ErrorStatusCode {
#[inline]
fn from(error: ClientErrorStatusCode) -> Self {
Self(error.0)
}
}
impl TryFrom<&'_ ErrorStatusCode> for ClientErrorStatusCode {
type Error = NotAClientError;
fn try_from(error: &ErrorStatusCode) -> Result<Self, Self::Error> {
error.as_client_error()
}
}
impl From<&'_ ClientErrorStatusCode> for ErrorStatusCode {
#[inline]
fn from(error: &ClientErrorStatusCode) -> Self {
Self(error.0)
}
}
impl PartialEq<ErrorStatusCode> for ClientErrorStatusCode {
#[inline]
fn eq(&self, other: &ErrorStatusCode) -> bool {
self.0 == other.0
}
}
impl PartialEq<ClientErrorStatusCode> for ErrorStatusCode {
#[inline]
fn eq(&self, other: &ClientErrorStatusCode) -> bool {
self.0 == other.0
}
}
#[derive(Debug, thiserror::Error)]
#[error("status code {0} is not a 4xx or 5xx error")]
pub struct NotAnError(http::StatusCode);
#[derive(Debug, thiserror::Error)]
#[error("status code {0} is not a 4xx client error")]
pub struct NotAClientError(http::StatusCode);
#[derive(Debug, thiserror::Error)]
pub enum InvalidErrorStatusCode {
#[error(transparent)]
NotAnError(#[from] NotAnError),
#[error(transparent)]
InvalidStatus(#[from] http::status::InvalidStatusCode),
}
#[derive(Debug, thiserror::Error)]
pub enum InvalidClientErrorStatusCode {
#[error(transparent)]
NotAClientError(#[from] NotAClientError),
#[error(transparent)]
InvalidStatus(#[from] http::status::InvalidStatusCode),
}