mod raise;
mod rescue;
mod result;
mod server;
use http::header::{CONTENT_LENGTH, CONTENT_TYPE};
use serde::{Serialize, Serializer};
use std::fmt::{self, Debug, Display, Formatter};
use std::io::{self, Error as IoError};
#[doc(hidden)]
pub use http::StatusCode;
pub use rescue::{Rescue, Sanitizer, rescue};
pub use result::ResultExt;
pub(crate) use server::ServerError;
use crate::response::Response;
use crate::router::MethodNotAllowed;
pub type BoxError = Box<dyn std::error::Error + Send + Sync>;
#[derive(Debug)]
pub struct Error {
status: StatusCode,
source: ErrorSource,
}
#[derive(Debug)]
enum ErrorSource {
AllowMethod(Box<MethodNotAllowed>),
Message(String),
Other(BoxError),
Json(serde_json::Error),
}
enum ErrorSourceRef<'a> {
AllowMethod(&'a MethodNotAllowed),
Message(&'a str),
Other(&'a (dyn std::error::Error + 'static)),
Json(&'a serde_json::Error),
}
#[derive(Serialize)]
#[serde(untagged)]
enum ErrorList<'a> {
Original(&'a str),
Chain(Vec<String>),
}
#[derive(Serialize)]
struct Errors<'a> {
#[serde(serialize_with = "serialize_status_code")]
status: StatusCode,
errors: ErrorList<'a>,
}
fn serialize_status_code<S>(status: &StatusCode, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_u16(status.as_u16())
}
impl Error {
pub fn new(message: impl Into<String>) -> Self {
Self {
status: StatusCode::INTERNAL_SERVER_ERROR,
source: ErrorSource::Message(message.into()),
}
}
pub fn other(source: BoxError) -> Self {
Self {
status: StatusCode::INTERNAL_SERVER_ERROR,
source: ErrorSource::Other(source),
}
}
pub fn from_io_error(error: IoError) -> Self {
let status = match error.kind() {
io::ErrorKind::AlreadyExists => StatusCode::CONFLICT,
io::ErrorKind::BrokenPipe
| io::ErrorKind::ConnectionReset
| io::ErrorKind::ConnectionAborted => StatusCode::BAD_GATEWAY,
io::ErrorKind::ConnectionRefused => StatusCode::SERVICE_UNAVAILABLE,
io::ErrorKind::InvalidData | io::ErrorKind::InvalidInput => StatusCode::BAD_REQUEST,
io::ErrorKind::IsADirectory
| io::ErrorKind::NotADirectory
| io::ErrorKind::PermissionDenied => StatusCode::FORBIDDEN,
io::ErrorKind::NotFound => StatusCode::NOT_FOUND,
io::ErrorKind::TimedOut => StatusCode::GATEWAY_TIMEOUT,
_ => StatusCode::INTERNAL_SERVER_ERROR,
};
Self::other_with_status(Box::new(error), status)
}
pub fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self.as_source() {
ErrorSourceRef::AllowMethod(source) => Some(source),
ErrorSourceRef::Other(source) => Some(source),
ErrorSourceRef::Json(source) => Some(source),
_ => None,
}
}
pub fn status(&self) -> StatusCode {
self.status
}
}
impl Error {
pub(crate) fn invalid_utf8_sequence(name: &str) -> Self {
let mut error = Self::new(format!("invalid utf-8 sequence of bytes in {}.", name));
error.status = StatusCode::BAD_REQUEST;
error
}
pub(crate) fn method_not_allowed(error: MethodNotAllowed) -> Self {
Self {
source: ErrorSource::AllowMethod(Box::new(error)),
status: StatusCode::METHOD_NOT_ALLOWED,
}
}
pub(crate) fn payload_too_large() -> Self {
let message = "request body exceeds the maximum length".to_owned();
Self {
source: ErrorSource::Message(message),
status: StatusCode::PAYLOAD_TOO_LARGE,
}
}
pub(crate) fn require_path_param(name: &str) -> Self {
let mut error = Self::new(format!("missing required path parameter: \"{}\".", name));
error.status = StatusCode::BAD_REQUEST;
error
}
pub(crate) fn require_query_param(name: &str) -> Self {
let mut error = Self::new(format!("missing required query parameter: \"{}\".", name));
error.status = StatusCode::BAD_REQUEST;
error
}
pub(crate) fn ser_json(source: serde_json::Error) -> Self {
Self {
status: StatusCode::INTERNAL_SERVER_ERROR,
source: ErrorSource::Json(source),
}
}
pub(crate) fn de_json(source: serde_json::Error) -> Self {
Self {
status: StatusCode::BAD_REQUEST,
source: ErrorSource::Json(source),
}
}
#[doc(hidden)]
pub fn new_with_status(message: impl Into<String>, status: StatusCode) -> Self {
let mut error = Self::new(message);
error.status = status;
error
}
#[doc(hidden)]
pub fn other_with_status(source: BoxError, status: StatusCode) -> Self {
let mut error = Self::other(source);
error.status = status;
error
}
#[inline]
fn as_source(&self) -> ErrorSourceRef<'_> {
match &self.source {
ErrorSource::AllowMethod(source) => ErrorSourceRef::AllowMethod(source),
ErrorSource::Message(message) => ErrorSourceRef::Message(message),
ErrorSource::Other(source) => ErrorSourceRef::Other(source.as_ref()),
ErrorSource::Json(source) => ErrorSourceRef::Json(source),
}
}
fn repr_json(&self) -> Errors<'_> {
if let ErrorSourceRef::Message(message) = self.as_source() {
Errors::new(self.status, message)
} else {
let mut errors = Vec::with_capacity(12);
let mut source = self.source();
while let Some(error) = source {
errors.push(error.to_string());
source = error.source();
}
errors.reverse();
Errors::chain(self.status, errors)
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self.as_source() {
ErrorSourceRef::Message(message) => Display::fmt(message, f),
ErrorSourceRef::AllowMethod(source) => Display::fmt(source, f),
ErrorSourceRef::Other(source) => Display::fmt(source, f),
ErrorSourceRef::Json(source) => Display::fmt(source, f),
}
}
}
impl From<Error> for BoxError {
fn from(error: Error) -> Self {
match error.source {
ErrorSource::AllowMethod(source) => source,
ErrorSource::Message(string) => string.into(),
ErrorSource::Other(source) => source,
ErrorSource::Json(source) => source.into(),
}
}
}
impl<E> From<E> for Error
where
E: std::error::Error + Send + Sync + 'static,
{
fn from(source: E) -> Self {
Self::other(Box::new(source))
}
}
impl From<Error> for Response {
fn from(error: Error) -> Self {
let message = error.to_string();
let content_len = message.len().into();
let mut response = Self::new(message.into());
*response.status_mut() = error.status;
let headers = response.headers_mut();
headers.insert(CONTENT_LENGTH, content_len);
if let Ok(content_type) = "text/plain; charset=utf-8".try_into() {
headers.insert(CONTENT_TYPE, content_type);
}
response
}
}
impl Serialize for Error {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.repr_json().serialize(serializer)
}
}
impl<'a> Errors<'a> {
fn new(status: StatusCode, message: &'a str) -> Self {
Self {
status,
errors: ErrorList::Original(message),
}
}
fn chain(status: StatusCode, chain: Vec<String>) -> Self {
Self {
status,
errors: ErrorList::Chain(chain),
}
}
}