use self::KindPriv::*;
use std::error;
use std::fmt;
use http::StatusCode;
#[derive(Debug)]
pub struct Builder {
kind: Option<(String, String)>,
detail: Option<String>,
status: Option<StatusCode>,
}
#[derive(Serialize)]
pub struct Error {
#[serde(rename = "type")]
kind: String,
title: String,
#[serde(skip_serializing_if = "Option::is_none")]
detail: Option<String>,
#[serde(skip)]
status: StatusCode,
#[serde(skip)]
error_kind: ErrorKind,
}
impl Builder {
fn new() -> Self {
Self {
kind: None,
detail: None,
status: None,
}
}
pub fn status(self, status: StatusCode) -> Self {
Self {
kind: self.kind,
detail: self.detail,
status: Some(status),
}
}
pub fn kind(self, kind: &str, title: &str) -> Self {
Self {
kind: Some((kind.to_owned(), title.to_owned())),
detail: self.detail,
status: self.status,
}
}
pub fn detail(self, detail: &str) -> Self {
Self {
kind: self.kind,
detail: Some(detail.to_owned()),
status: self.status,
}
}
pub fn build(self) -> Error {
let mut err =
match (self.kind, self.status) {
(Some((ref kind, ref title)), Some(status)) => Error::new(kind, title, status),
(Some(_), None) => Error::from(StatusCode::INTERNAL_SERVER_ERROR),
(None, Some(status)) => Error::from(status),
(None, None) => Error::from(StatusCode::INTERNAL_SERVER_ERROR),
};
match self.detail {
Some(ref detail) => {
err.set_detail(detail);
err
},
None => err,
}
}
}
impl Error {
#[deprecated(note="return value of the function will be changed to &str")]
pub fn kind(&self) -> &ErrorKind {
&self.error_kind
}
pub fn new(kind: &str, title: &str, status: StatusCode) -> Self {
Self {
kind: kind.to_owned(),
title: title.to_owned(),
detail: None,
status,
error_kind: ErrorKind::new(&status),
}
}
pub fn set_detail(&mut self, value: &str) -> &mut Self {
self.detail = Some(value.to_owned());
self
}
pub fn status_code(&self) -> StatusCode {
self.status
}
pub fn builder() -> Builder {
Builder::new()
}
}
impl error::Error for Error {
fn description(&self) -> &str {
self.status_code().canonical_reason().unwrap_or("Unknown status code")
}
}
impl fmt::Debug for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Error")
.field("kind", &self.kind)
.field("title", &self.title)
.field("detail", &self.detail)
.field("status", &self.status)
.finish()
}
}
impl fmt::Display for Error {
#[allow(deprecated)]
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(
fmt,
"[{}] {}",
self.kind, self.title,
)?;
if let Some(ref detail) = self.detail {
write!(fmt, ": {}", detail)?;
}
use std::error::Error;
if let Some(ref cause) = self.cause() {
write!(fmt, ": {}", cause)?;
}
Ok(())
}
}
impl From<StatusCode> for Error {
fn from(status: StatusCode) -> Self {
let title = status.canonical_reason().unwrap_or("Unknown status code");
Self {
kind: String::from("about:blank"),
title: title.to_owned(),
detail: None,
status,
error_kind: ErrorKind::new(&status),
}
}
}
pub struct ErrorKind {
kind: KindPriv,
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
enum KindPriv {
BadRequest,
Unauthorized,
Fordidden,
NotFound,
UnprocessableEntity,
Internal,
}
impl From<ErrorKind> for Error {
fn from(error_kind: ErrorKind) -> Error {
error_kind.status_code().into()
}
}
impl ErrorKind {
#[deprecated(note="please use 'Error::from(http::StatusCode::BAD_REQUEST)' instead")]
pub fn bad_request() -> ErrorKind {
ErrorKind { kind: BadRequest }
}
#[deprecated(note="please use 'kind() == http::StatusCode::BAD_REQUEST' instead")]
pub fn is_bad_request(&self) -> bool {
self.kind == BadRequest
}
#[deprecated(note="please use 'Error::from(http::StatusCode::UNAUTHORIZED)' instead")]
pub fn unauthorized() -> ErrorKind {
ErrorKind { kind: Unauthorized }
}
#[deprecated(note="please use 'Error::from(http::StatusCode::FORBIDDEN)' instead")]
pub fn forbidden() -> ErrorKind {
ErrorKind { kind: Fordidden }
}
#[deprecated(note="please use 'Error::from(http::StatusCode::NOT_FOUND)' instead")]
pub fn not_found() -> ErrorKind {
ErrorKind { kind: NotFound }
}
#[deprecated(note="please use 'kind() == http::StatusCode::NOT_FOUND' instead")]
pub fn is_not_found(&self) -> bool {
self.kind == NotFound
}
#[deprecated(note="please use 'Error::from(http::StatusCode::UNPROCESSABLE_ENTITY)' instead")]
pub fn unprocessable_entity() -> ErrorKind {
ErrorKind { kind: UnprocessableEntity }
}
#[deprecated(note="please use 'Error::from(http::StatusCode::INTERNAL_SERVER_ERROR)' instead")]
pub fn internal() -> ErrorKind {
ErrorKind { kind: Internal }
}
#[deprecated(note="please use 'kind() == http::StatusCode::INTERNAL_SERVER_ERROR' instead")]
pub fn is_internal(&self) -> bool {
self.kind == Internal
}
fn status_code(&self) -> StatusCode {
match self.kind {
BadRequest => StatusCode::BAD_REQUEST,
Unauthorized => StatusCode::UNAUTHORIZED,
Fordidden => StatusCode::FORBIDDEN,
NotFound => StatusCode::NOT_FOUND,
UnprocessableEntity => StatusCode::UNPROCESSABLE_ENTITY,
Internal => StatusCode::INTERNAL_SERVER_ERROR,
}
}
fn new(status: &StatusCode) -> Self {
match status {
&StatusCode::BAD_REQUEST => ErrorKind {
kind: BadRequest
},
&StatusCode::UNAUTHORIZED => ErrorKind {
kind: Unauthorized
},
&StatusCode::FORBIDDEN => ErrorKind {
kind: Fordidden
},
&StatusCode::NOT_FOUND => ErrorKind {
kind: NotFound
},
&StatusCode::UNPROCESSABLE_ENTITY => ErrorKind {
kind: UnprocessableEntity
},
_ => ErrorKind {
kind: Internal
},
}
}
}
impl fmt::Debug for ErrorKind {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self.kind {
BadRequest => "ErrorKind::BadRequest",
Unauthorized => "ErrorKind::Unauthorized",
Fordidden => "ErrorKind::Forbidden",
NotFound => "ErrorKind::NotFound",
UnprocessableEntity => "ErrorKind::UnprocessableEntity",
Internal => "ErrorKind::Internal",
}.fmt(fmt)
}
}