use std::{
error::Error as StdError,
fmt::{self, Debug, Display, Formatter, Write},
};
use http::StatusCode;
macro_rules! default_errors {
(
$(
$(#[$docs:meta])*
$sname:ident, $code:expr, $name:expr, $brief:expr);
+) =>
{
$(
#[doc=concat!($brief,"\n ")]
$(#[$docs])*
#[must_use] pub fn $sname() -> StatusError {
StatusError {
code: $code,
name: $name.into(),
brief: $brief.into(),
detail: None,
error: None,
}
}
)+
}
}
#[derive(Debug)]
#[non_exhaustive]
pub struct StatusError {
pub code: StatusCode,
pub name: String,
pub brief: String,
pub detail: Option<String>,
pub error: Option<Box<dyn StdError + Sync + Send + 'static>>,
}
impl StatusError {
#[must_use]
pub fn brief(mut self, brief: impl Into<String>) -> Self {
self.brief = brief.into();
self
}
#[must_use]
pub fn detail(mut self, detail: impl Into<String>) -> Self {
self.detail = Some(detail.into());
self
}
#[must_use]
pub fn error<T>(mut self, error: T) -> Self
where
T: Into<Box<dyn StdError + Sync + Send + 'static>>,
{
self.error = Some(error.into());
self
}
#[must_use]
pub fn detailed_display(&self) -> String {
let mut str_error = format!(
"code: {} name: {} brief: {}",
self.code, self.name, self.brief
);
if let Some(detail) = &self.detail {
write!(&mut str_error, " detail: {detail}").ok();
}
if let Some(error) = &self.error {
write!(&mut str_error, " error: {error}").ok();
}
str_error
}
default_errors! {
bad_request, StatusCode::BAD_REQUEST, "Bad Request", "The request could not be understood by the server due to malformed syntax.";
unauthorized, StatusCode::UNAUTHORIZED, "Unauthorized", "The request requires user authentication.";
payment_required, StatusCode::PAYMENT_REQUIRED, "Payment Required", "The request could not be processed due to lack of payment.";
forbidden, StatusCode::FORBIDDEN, "Forbidden", "The server refused to authorize the request.";
not_found, StatusCode::NOT_FOUND, "Not Found", "The requested resource could not be found.";
method_not_allowed, StatusCode::METHOD_NOT_ALLOWED, "Method Not Allowed", "The request method is not supported for the requested resource.";
not_acceptable, StatusCode::NOT_ACCEPTABLE, "Not Acceptable", "The requested resource is capable of generating only content not acceptable according to the Accept headers sent in the request.";
proxy_authentication_required, StatusCode::PROXY_AUTHENTICATION_REQUIRED, "Proxy Authentication Required", "Authentication with the proxy is required.";
request_timeout, StatusCode::REQUEST_TIMEOUT, "Request Timeout", "The server timed out waiting for the request.";
conflict, StatusCode::CONFLICT, "Conflict", "The request could not be processed because of a conflict in the request.";
gone, StatusCode::GONE, "Gone", "The resource requested is no longer available and will not be available again.";
length_required, StatusCode::LENGTH_REQUIRED, "Length Required", "The request did not specify the length of its content, which is required by the requested resource.";
precondition_failed, StatusCode::PRECONDITION_FAILED, "Precondition Failed", "The server does not meet one of the preconditions specified in the request.";
payload_too_large, StatusCode::PAYLOAD_TOO_LARGE, "Payload Too Large", "The request is larger than the server is willing or able to process.";
uri_too_long, StatusCode::URI_TOO_LONG, "URI Too Long", "The URI provided was too long for the server to process.";
unsupported_media_type, StatusCode::UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type", "The request entity has a media type which the server or resource does not support.";
range_not_satisfiable, StatusCode::RANGE_NOT_SATISFIABLE, "Range Not Satisfiable", "The portion of the requested file cannot be supplied by the server.";
expectation_failed, StatusCode::EXPECTATION_FAILED, "Expectation Failed", "The server cannot meet the requirements of the expect request-header field.";
im_a_teapot, StatusCode::IM_A_TEAPOT, "I'm a teapot", "I was requested to brew coffee, and I am a teapot.";
misdirected_request, StatusCode::MISDIRECTED_REQUEST, "Misdirected Request", "The server cannot produce a response for this request.";
unprocessable_entity, StatusCode::UNPROCESSABLE_ENTITY, "Unprocessable Entity", "The request was well-formed but was unable to be followed due to semantic errors.";
locked, StatusCode::LOCKED, "Locked", "The source or destination resource of a method is locked.";
failed_dependency, StatusCode::FAILED_DEPENDENCY, "Failed Dependency", "The method could not be performed on the resource because the requested action depended on another action and that action failed.";
upgrade_required, StatusCode::UPGRADE_REQUIRED, "Upgrade Required", "Switching to the protocol in the Upgrade header field is required.";
precondition_required, StatusCode::PRECONDITION_REQUIRED, "Precondition Required", "The server requires the request to be conditional.";
too_many_requests, StatusCode::TOO_MANY_REQUESTS, "Too Many Requests", "Too many requests have been received recently.";
request_header_fields_toolarge, StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE, "Request Header Fields Too Large", "The server is unwilling to process the request because either an individual header field, or all the header fields collectively, are too large.";
unavailable_for_legalreasons, StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS, "Unavailable For Legal Reasons", "The requested resource is unavailable due to a legal demand to deny access to this resource.";
internal_server_error, StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error", "The server encountered an internal error while processing this request.";
not_implemented, StatusCode::NOT_IMPLEMENTED, "Not Implemented", "The server either does not recognize the request method, or it lacks the ability to fulfill the request.";
bad_gateway, StatusCode::BAD_GATEWAY, "Bad Gateway", "Received an invalid response from an inbound server it accessed while attempting to fulfill the request.";
service_unavailable, StatusCode::SERVICE_UNAVAILABLE, "Service Unavailable", "The server is currently unavailable.";
gateway_timeout, StatusCode::GATEWAY_TIMEOUT, "Gateway Timeout", "The server did not receive a timely response from an upstream server.";
http_version_not_supported, StatusCode::HTTP_VERSION_NOT_SUPPORTED, "HTTP Version Not Supported", "The server does not support, or refuses to support, the major version of HTTP that was used in the request message.";
variant_also_negotiates, StatusCode::VARIANT_ALSO_NEGOTIATES, "Variant Also Negotiates", "The server has an internal configuration error.";
insufficient_storage, StatusCode::INSUFFICIENT_STORAGE, "Insufficient Storage", "The method could not be performed on the resource because the server is unable to store the representation needed to successfully complete the request.";
loop_detected, StatusCode::LOOP_DETECTED, "Loop Detected", "the server terminated an operation because it encountered an infinite loop while processing a request with \"Depth: infinity\".";
not_extended, StatusCode::NOT_EXTENDED, "Not Extended", "Further extensions to the request are required for the server to fulfill it.";
network_authentication_required, StatusCode::NETWORK_AUTHENTICATION_REQUIRED, "Network Authentication Required", "the client needs to authenticate to gain network access."
}
}
impl StdError for StatusError {}
impl Display for StatusError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "[{} {}] brief: {}", self.code, self.name, self.brief)
}
}
impl StatusError {
#[must_use]
pub fn from_code(code: StatusCode) -> Option<Self> {
match code {
StatusCode::BAD_REQUEST => Some(Self::bad_request()),
StatusCode::UNAUTHORIZED => Some(Self::unauthorized()),
StatusCode::PAYMENT_REQUIRED => Some(Self::payment_required()),
StatusCode::FORBIDDEN => Some(Self::forbidden()),
StatusCode::NOT_FOUND => Some(Self::not_found()),
StatusCode::METHOD_NOT_ALLOWED => Some(Self::method_not_allowed()),
StatusCode::NOT_ACCEPTABLE => Some(Self::not_acceptable()),
StatusCode::PROXY_AUTHENTICATION_REQUIRED => {
Some(Self::proxy_authentication_required())
}
StatusCode::REQUEST_TIMEOUT => Some(Self::request_timeout()),
StatusCode::CONFLICT => Some(Self::conflict()),
StatusCode::GONE => Some(Self::gone()),
StatusCode::LENGTH_REQUIRED => Some(Self::length_required()),
StatusCode::PRECONDITION_FAILED => Some(Self::precondition_failed()),
StatusCode::PAYLOAD_TOO_LARGE => Some(Self::payload_too_large()),
StatusCode::URI_TOO_LONG => Some(Self::uri_too_long()),
StatusCode::UNSUPPORTED_MEDIA_TYPE => Some(Self::unsupported_media_type()),
StatusCode::RANGE_NOT_SATISFIABLE => Some(Self::range_not_satisfiable()),
StatusCode::EXPECTATION_FAILED => Some(Self::expectation_failed()),
StatusCode::IM_A_TEAPOT => Some(Self::im_a_teapot()),
StatusCode::MISDIRECTED_REQUEST => Some(Self::misdirected_request()),
StatusCode::UNPROCESSABLE_ENTITY => Some(Self::unprocessable_entity()),
StatusCode::LOCKED => Some(Self::locked()),
StatusCode::FAILED_DEPENDENCY => Some(Self::failed_dependency()),
StatusCode::UPGRADE_REQUIRED => Some(Self::upgrade_required()),
StatusCode::PRECONDITION_REQUIRED => Some(Self::precondition_required()),
StatusCode::TOO_MANY_REQUESTS => Some(Self::too_many_requests()),
StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE => {
Some(Self::request_header_fields_toolarge())
}
StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS => Some(Self::unavailable_for_legalreasons()),
StatusCode::INTERNAL_SERVER_ERROR => Some(Self::internal_server_error()),
StatusCode::NOT_IMPLEMENTED => Some(Self::not_implemented()),
StatusCode::BAD_GATEWAY => Some(Self::bad_gateway()),
StatusCode::SERVICE_UNAVAILABLE => Some(Self::service_unavailable()),
StatusCode::GATEWAY_TIMEOUT => Some(Self::gateway_timeout()),
StatusCode::HTTP_VERSION_NOT_SUPPORTED => Some(Self::http_version_not_supported()),
StatusCode::VARIANT_ALSO_NEGOTIATES => Some(Self::variant_also_negotiates()),
StatusCode::INSUFFICIENT_STORAGE => Some(Self::insufficient_storage()),
StatusCode::LOOP_DETECTED => Some(Self::loop_detected()),
StatusCode::NOT_EXTENDED => Some(Self::not_extended()),
StatusCode::NETWORK_AUTHENTICATION_REQUIRED => {
Some(Self::network_authentication_required())
}
_ => None,
}
}
}