finchers_core/
error.rs

1//! Error primitives.
2
3use std::borrow::Cow;
4use std::fmt;
5
6use either::Either;
7use failure::{self, Fail};
8use http::StatusCode;
9use http::header::{HeaderMap, HeaderValue};
10
11/// Trait representing error values from endpoints.
12///
13/// The types which implements this trait will be implicitly converted to an HTTP response
14/// by the runtime.
15pub trait HttpError: fmt::Debug + fmt::Display + Send + Sync + 'static {
16    /// Return the HTTP status code associated with this error type.
17    fn status_code(&self) -> StatusCode;
18
19    /// Append a set of header values to the header map.
20    #[allow(unused_variables)]
21    fn append_headers(&self, headers: &mut HeaderMap<HeaderValue>) {}
22
23    /// Return the reference to a value of `Fail` if exists.
24    fn as_fail(&self) -> Option<&Fail> {
25        None
26    }
27}
28
29impl<L, R> HttpError for Either<L, R>
30where
31    L: HttpError,
32    R: HttpError,
33{
34    fn status_code(&self) -> StatusCode {
35        match *self {
36            Either::Left(ref e) => e.status_code(),
37            Either::Right(ref e) => e.status_code(),
38        }
39    }
40
41    fn as_fail(&self) -> Option<&Fail> {
42        match *self {
43            Either::Left(ref e) => e.as_fail(),
44            Either::Right(ref e) => e.as_fail(),
45        }
46    }
47}
48
49/// An HTTP error which represents `400 Bad Request`.
50pub struct BadRequest {
51    inner: Either<Cow<'static, str>, failure::Error>,
52}
53
54impl<E: Into<failure::Error>> From<E> for BadRequest {
55    fn from(fail: E) -> Self {
56        BadRequest::from_fail(fail)
57    }
58}
59
60impl BadRequest {
61    #[allow(missing_docs)]
62    pub fn new<S>(message: S) -> BadRequest
63    where
64        S: Into<Cow<'static, str>>,
65    {
66        BadRequest {
67            inner: Either::Left(message.into()),
68        }
69    }
70
71    #[allow(missing_docs)]
72    pub fn from_fail<E>(fail: E) -> BadRequest
73    where
74        E: Into<failure::Error>,
75    {
76        BadRequest {
77            inner: Either::Right(Into::into(fail)),
78        }
79    }
80}
81
82impl fmt::Debug for BadRequest {
83    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84        match self.inner {
85            Either::Left(ref message) => f.debug_tuple("BadRequest").field(message).finish(),
86            Either::Right(ref err) => f.debug_tuple("BadRequest").field(err).finish(),
87        }
88    }
89}
90
91impl fmt::Display for BadRequest {
92    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93        match self.inner {
94            Either::Left(ref message) => f.write_str(message),
95            Either::Right(ref e) => fmt::Display::fmt(e, f),
96        }
97    }
98}
99
100impl HttpError for BadRequest {
101    fn status_code(&self) -> StatusCode {
102        StatusCode::BAD_REQUEST
103    }
104
105    fn as_fail(&self) -> Option<&Fail> {
106        self.inner.as_ref().right().map(failure::Error::cause)
107    }
108}
109
110/// An HTTP error which represents `500 Internal Server Error`
111pub struct ServerError {
112    inner: Either<Cow<'static, str>, failure::Error>,
113}
114
115impl<E: Into<failure::Error>> From<E> for ServerError {
116    fn from(fail: E) -> Self {
117        ServerError::from_fail(fail)
118    }
119}
120
121impl ServerError {
122    #[allow(missing_docs)]
123    pub fn new<S>(message: S) -> ServerError
124    where
125        S: Into<Cow<'static, str>>,
126    {
127        ServerError {
128            inner: Either::Left(message.into()),
129        }
130    }
131
132    #[allow(missing_docs)]
133    pub fn from_fail<E>(fail: E) -> ServerError
134    where
135        E: Into<failure::Error>,
136    {
137        ServerError {
138            inner: Either::Right(Into::into(fail)),
139        }
140    }
141}
142
143impl fmt::Debug for ServerError {
144    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
145        match self.inner {
146            Either::Left(ref message) => f.debug_tuple("ServerError").field(message).finish(),
147            Either::Right(ref err) => f.debug_tuple("ServerError").field(err).finish(),
148        }
149    }
150}
151
152impl fmt::Display for ServerError {
153    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
154        match self.inner {
155            Either::Left(ref message) => f.write_str(message),
156            Either::Right(ref e) => fmt::Display::fmt(e, f),
157        }
158    }
159}
160
161impl HttpError for ServerError {
162    fn status_code(&self) -> StatusCode {
163        StatusCode::INTERNAL_SERVER_ERROR
164    }
165
166    fn as_fail(&self) -> Option<&Fail> {
167        self.inner.as_ref().right().map(failure::Error::cause)
168    }
169}
170
171/// An error type indicating that a necessary elements was not given from the client.
172///
173/// This error value will return `400 Bad Request` as the HTTP status code.
174#[derive(Debug)]
175pub struct NotPresent {
176    message: Cow<'static, str>,
177}
178
179impl NotPresent {
180    #[allow(missing_docs)]
181    pub fn new<S>(message: S) -> NotPresent
182    where
183        S: Into<Cow<'static, str>>,
184    {
185        NotPresent {
186            message: message.into(),
187        }
188    }
189}
190
191impl fmt::Display for NotPresent {
192    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
193        f.write_str(&*self.message)
194    }
195}
196
197impl HttpError for NotPresent {
198    fn status_code(&self) -> StatusCode {
199        StatusCode::BAD_REQUEST
200    }
201}
202
203#[derive(Debug, Fail)]
204#[fail(display = "no route")]
205struct NoRoute;
206
207impl HttpError for NoRoute {
208    fn status_code(&self) -> StatusCode {
209        StatusCode::NOT_FOUND
210    }
211}
212
213/// A type which holds a value of `HttpError` in a type-erased form.
214#[derive(Debug)]
215pub struct Error {
216    inner: Either<Box<HttpError>, NoRoute>,
217}
218
219impl<E: HttpError> From<E> for Error {
220    fn from(err: E) -> Self {
221        Error {
222            inner: Either::Left(Box::new(err)),
223        }
224    }
225}
226
227impl fmt::Display for Error {
228    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
229        match self.inner {
230            Either::Left(ref e) => fmt::Display::fmt(&*e, f),
231            Either::Right(ref e) => fmt::Display::fmt(e, f),
232        }
233    }
234}
235
236impl Error {
237    pub(crate) fn skipped() -> Error {
238        Error {
239            inner: Either::Right(NoRoute),
240        }
241    }
242
243    #[allow(missing_docs)]
244    pub fn is_skipped(&self) -> bool {
245        self.inner.is_right()
246    }
247
248    /// Returns the reference to inner `HttpError`.
249    pub fn as_http_error(&self) -> &HttpError {
250        match self.inner {
251            Either::Left(ref e) => &**e,
252            Either::Right(ref e) => &*e as &HttpError,
253        }
254    }
255}