saphir/
error.rs

1use crate::{
2    http_context::HttpContext,
3    responder::{DynResponder, Responder},
4    response::Builder,
5};
6use http::{
7    header::{InvalidHeaderValue, ToStrError},
8    Error as HttpCrateError,
9};
10use hyper::Error as HyperError;
11use std::{
12    error::Error as StdError,
13    fmt::{Debug, Formatter},
14    io::Error as IoError,
15};
16use thiserror::Error;
17
18/// Type representing an internal error inerrant to the underlining logic behind
19/// saphir
20#[derive(Error, Debug)]
21pub enum InternalError {
22    #[error("Http: {0}")]
23    Http(HttpCrateError),
24    #[error("Hyper: {0}")]
25    Hyper(HyperError),
26    #[error("ToStr: {0}")]
27    ToStr(ToStrError),
28    #[error("Stack")]
29    Stack,
30}
31
32/// Error type throughout the saphir stack
33#[derive(Error)]
34pub enum SaphirError {
35    ///
36    #[error("Internal: {0}")]
37    Internal(#[from] InternalError),
38    ///
39    #[error("Io: {0}")]
40    Io(#[from] IoError),
41    /// Body was taken and cannot be polled
42    #[error("Body already taken")]
43    BodyAlreadyTaken,
44    /// The request was moved by a middleware without ending the request
45    /// processing
46    #[error("Request moved before handler")]
47    RequestMovedBeforeHandler,
48    /// The response was moved before being sent to the client
49    #[error("Response moved")]
50    ResponseMoved,
51    /// Custom error type to map any other error
52    #[error("Custom: {0}")]
53    Custom(Box<dyn StdError + Send + Sync + 'static>),
54    /// Custom error type to map any other error
55    #[error("Responder")]
56    Responder(Box<dyn DynResponder + Send + Sync + 'static>),
57    ///
58    #[error("Other: {0}")]
59    Other(String),
60    /// Error from (de)serializing json data
61    #[cfg(feature = "json")]
62    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
63    #[error("SerdeJson: {0}")]
64    SerdeJson(#[from] serde_json::error::Error),
65    /// Error from deserializing form data
66    #[cfg(feature = "form")]
67    #[cfg_attr(docsrs, doc(cfg(feature = "form")))]
68    #[error("SerdeUrlDe: {0}")]
69    SerdeUrlDe(#[from] serde_urlencoded::de::Error),
70    /// Error from serializing form data
71    #[cfg(feature = "form")]
72    #[cfg_attr(docsrs, doc(cfg(feature = "form")))]
73    #[error("SerdeUrlSer: {0}")]
74    SerdeUrlSer(#[from] serde_urlencoded::ser::Error),
75    ///
76    #[error("Missing parameter `{0}` (is_query: {1})")]
77    MissingParameter(String, bool),
78    ///
79    #[error("Invalid parameter `{0}` (is_query: {1})")]
80    InvalidParameter(String, bool),
81    ///
82    #[error("Request timed out")]
83    RequestTimeout,
84    /// Attempted to build stack twice
85    #[error("Stack alrealy initialized")]
86    StackAlreadyInitialized,
87    ///
88    #[error("Too many requests")]
89    TooManyRequests,
90    /// Validator error
91    #[cfg(feature = "validate-requests")]
92    #[cfg_attr(docsrs, doc(cfg(feature = "validate-requests")))]
93    #[error("ValidationErrors: {0}")]
94    ValidationErrors(#[from] validator::ValidationErrors),
95}
96
97impl Debug for SaphirError {
98    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
99        match self {
100            SaphirError::Internal(d) => std::fmt::Debug::fmt(d, f),
101            SaphirError::Io(d) => std::fmt::Debug::fmt(d, f),
102            SaphirError::BodyAlreadyTaken => f.write_str("BodyAlreadyTaken"),
103            SaphirError::RequestMovedBeforeHandler => f.write_str("RequestMovedBeforeHandler"),
104            SaphirError::ResponseMoved => f.write_str("ResponseMoved"),
105            SaphirError::Custom(d) => std::fmt::Debug::fmt(d, f),
106            SaphirError::Responder(_) => f.write_str("Responder"),
107            SaphirError::Other(d) => std::fmt::Debug::fmt(d, f),
108            #[cfg(feature = "json")]
109            SaphirError::SerdeJson(d) => std::fmt::Debug::fmt(d, f),
110            #[cfg(feature = "form")]
111            SaphirError::SerdeUrlDe(d) => std::fmt::Debug::fmt(d, f),
112            #[cfg(feature = "form")]
113            SaphirError::SerdeUrlSer(d) => std::fmt::Debug::fmt(d, f),
114            SaphirError::MissingParameter(d, _) => std::fmt::Debug::fmt(d, f),
115            SaphirError::InvalidParameter(d, _) => std::fmt::Debug::fmt(d, f),
116            SaphirError::RequestTimeout => f.write_str("RequestTimeout"),
117            SaphirError::StackAlreadyInitialized => f.write_str("StackAlreadyInitialized"),
118            SaphirError::TooManyRequests => f.write_str("TooManyRequests"),
119            #[cfg(feature = "validate-requests")]
120            SaphirError::ValidationErrors(d) => std::fmt::Debug::fmt(d, f),
121        }
122    }
123}
124
125impl SaphirError {
126    pub fn responder<T: Responder + Send + Sync + 'static>(e: T) -> Self {
127        SaphirError::Responder(Box::new(Some(e)))
128    }
129
130    pub(crate) fn response_builder(self, builder: Builder, ctx: &HttpContext) -> Builder {
131        match self {
132            SaphirError::Internal(_) => builder.status(500),
133            SaphirError::Io(_) => builder.status(500),
134            SaphirError::BodyAlreadyTaken => builder.status(500),
135            SaphirError::Custom(_) => builder.status(500),
136            SaphirError::Other(_) => builder.status(500),
137            #[cfg(feature = "json")]
138            SaphirError::SerdeJson(_) => builder.status(400),
139            #[cfg(feature = "form")]
140            SaphirError::SerdeUrlDe(_) => builder.status(400),
141            #[cfg(feature = "form")]
142            SaphirError::SerdeUrlSer(_) => builder.status(400),
143            SaphirError::MissingParameter(..) => builder.status(400),
144            SaphirError::InvalidParameter(..) => builder.status(400),
145            SaphirError::RequestMovedBeforeHandler => builder.status(500),
146            SaphirError::ResponseMoved => builder.status(500),
147            SaphirError::Responder(mut r) => r.dyn_respond(builder, ctx),
148            SaphirError::RequestTimeout => builder.status(408),
149            SaphirError::StackAlreadyInitialized => builder.status(500),
150            SaphirError::TooManyRequests => builder.status(429),
151            #[cfg(feature = "validate-requests")]
152            SaphirError::ValidationErrors(_) => builder.status(400),
153        }
154    }
155
156    #[allow(unused_variables)]
157    pub(crate) fn log(&self, ctx: &HttpContext) {
158        let op_id = {
159            #[cfg(not(feature = "operation"))]
160            {
161                String::new()
162            }
163
164            #[cfg(feature = "operation")]
165            {
166                format!("[Operation id: {}] ", ctx.operation_id)
167            }
168        };
169
170        match self {
171            SaphirError::Internal(e) => {
172                warn!("{}Saphir encountered an internal error that was returned as a responder: {:?}", op_id, e);
173            }
174            SaphirError::Io(e) => {
175                warn!("{}Saphir encountered an Io error that was returned as a responder: {:?}", op_id, e);
176            }
177            SaphirError::BodyAlreadyTaken => {
178                warn!("{}A controller handler attempted to take the request body more thant one time", op_id);
179            }
180            SaphirError::Custom(e) => {
181                warn!("{}A custom error was returned as a responder: {:?}", op_id, e);
182            }
183            SaphirError::Other(e) => {
184                warn!("{}Saphir encountered an Unknown error that was returned as a responder: {:?}", op_id, e);
185            }
186            #[cfg(feature = "json")]
187            SaphirError::SerdeJson(e) => {
188                debug!("{}Unable to de/serialize json type: {:?}", op_id, e);
189            }
190            #[cfg(feature = "form")]
191            SaphirError::SerdeUrlDe(e) => {
192                debug!("{}Unable to deserialize form type: {:?}", op_id, e);
193            }
194            #[cfg(feature = "form")]
195            SaphirError::SerdeUrlSer(e) => {
196                debug!("{}Unable to serialize form type: {:?}", op_id, e);
197            }
198            SaphirError::MissingParameter(name, is_query) => {
199                if *is_query {
200                    debug!("{}Missing query parameter {}", op_id, name);
201                } else {
202                    debug!("{}Missing path parameter {}", op_id, name);
203                }
204            }
205            SaphirError::InvalidParameter(name, is_query) => {
206                if *is_query {
207                    debug!("{}Unable to parse query parameter {}", op_id, name);
208                } else {
209                    debug!("{}Unable to parse path parameter {}", op_id, name);
210                }
211            }
212            SaphirError::RequestMovedBeforeHandler => {
213                warn!(
214                    "{}A request was moved out of its context by a middleware, but the middleware did not stop request processing",
215                    op_id
216                );
217            }
218            SaphirError::ResponseMoved => {
219                warn!("{}A response was moved before being sent to the client", op_id);
220            }
221            SaphirError::RequestTimeout => {
222                warn!("{}Request timed out", op_id);
223            }
224            SaphirError::Responder(_) => {}
225            SaphirError::StackAlreadyInitialized => {
226                warn!("{}Attempted to initialize stack twice", op_id);
227            }
228            SaphirError::TooManyRequests => {
229                warn!("{}Made too many requests", op_id);
230            }
231            #[cfg(feature = "validate-requests")]
232            SaphirError::ValidationErrors(e) => {
233                debug!("{}Validation error: {:?}", op_id, e);
234            }
235        }
236    }
237}
238
239impl From<HttpCrateError> for SaphirError {
240    fn from(e: HttpCrateError) -> Self {
241        SaphirError::Internal(InternalError::Http(e))
242    }
243}
244
245impl From<InvalidHeaderValue> for SaphirError {
246    fn from(e: InvalidHeaderValue) -> Self {
247        SaphirError::Internal(InternalError::Http(HttpCrateError::from(e)))
248    }
249}
250
251impl From<HyperError> for SaphirError {
252    fn from(e: HyperError) -> Self {
253        SaphirError::Internal(InternalError::Hyper(e))
254    }
255}
256
257impl From<ToStrError> for SaphirError {
258    fn from(e: ToStrError) -> Self {
259        SaphirError::Internal(InternalError::ToStr(e))
260    }
261}
262
263impl Responder for SaphirError {
264    #[allow(unused_variables)]
265    fn respond_with_builder(self, builder: Builder, ctx: &HttpContext) -> Builder {
266        self.log(ctx);
267        self.response_builder(builder, ctx)
268    }
269}