reduct_base/
error.rs

1// Copyright 2023 ReductSoftware UG
2// This Source Code Form is subject to the terms of the Mozilla Public
3//    License, v. 2.0. If a copy of the MPL was not distributed with this
4//    file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
6pub use int_enum::IntEnum;
7use std::error::Error;
8use std::fmt::{Debug, Display, Error as FmtError, Formatter};
9use std::sync::PoisonError;
10use std::time::SystemTimeError;
11use url::ParseError;
12
13/// HTTP status codes + client errors (negative).
14#[repr(i16)]
15#[derive(Debug, PartialEq, PartialOrd, Copy, Clone, IntEnum)]
16pub enum ErrorCode {
17    UrlParseError = -4,
18    ConnectionError = -3,
19    Timeout = -2,
20    Unknown = -1,
21
22    Continue = 100,
23    OK = 200,
24    Created = 201,
25    Accepted = 202,
26    NoContent = 204,
27    BadRequest = 400,
28    Unauthorized = 401,
29    Forbidden = 403,
30    NotFound = 404,
31    MethodNotAllowed = 405,
32    NotAcceptable = 406,
33    RequestTimeout = 408,
34    Conflict = 409,
35    Gone = 410,
36    LengthRequired = 411,
37    PreconditionFailed = 412,
38    PayloadTooLarge = 413,
39    URITooLong = 414,
40    UnsupportedMediaType = 415,
41    RangeNotSatisfiable = 416,
42    ExpectationFailed = 417,
43    ImATeapot = 418,
44    MisdirectedRequest = 421,
45    UnprocessableEntity = 422,
46    Locked = 423,
47    FailedDependency = 424,
48    TooEarly = 425,
49    UpgradeRequired = 426,
50    PreconditionRequired = 428,
51    TooManyRequests = 429,
52    RequestHeaderFieldsTooLarge = 431,
53    UnavailableForLegalReasons = 451,
54    InternalServerError = 500,
55    NotImplemented = 501,
56    BadGateway = 502,
57    ServiceUnavailable = 503,
58    GatewayTimeout = 504,
59    HTTPVersionNotSupported = 505,
60    VariantAlsoNegotiates = 506,
61    InsufficientStorage = 507,
62    LoopDetected = 508,
63    NotExtended = 510,
64    NetworkAuthenticationRequired = 511,
65}
66
67/// An HTTP error, we use it for error handling.
68#[derive(PartialEq, Debug, Clone)]
69pub struct ReductError {
70    /// The HTTP status code.
71    pub status: ErrorCode,
72
73    /// The human readable message.
74    pub message: String,
75}
76
77impl Display for ReductError {
78    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
79        write!(f, "[{:?}] {}", self.status, self.message)
80    }
81}
82
83impl Display for ErrorCode {
84    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
85        write!(f, "{}", self.int_value())
86    }
87}
88
89impl From<std::io::Error> for ReductError {
90    fn from(err: std::io::Error) -> Self {
91        // An IO error is an internal reductstore error
92        ReductError {
93            status: ErrorCode::InternalServerError,
94            message: err.to_string(),
95        }
96    }
97}
98
99impl From<SystemTimeError> for ReductError {
100    fn from(err: SystemTimeError) -> Self {
101        // A system time error is an internal reductstore error
102        ReductError {
103            status: ErrorCode::InternalServerError,
104            message: err.to_string(),
105        }
106    }
107}
108
109impl From<ParseError> for ReductError {
110    fn from(err: ParseError) -> Self {
111        // A parse error is an internal reductstore error
112        ReductError {
113            status: ErrorCode::UrlParseError,
114            message: err.to_string(),
115        }
116    }
117}
118
119impl<T> From<PoisonError<T>> for ReductError {
120    fn from(_: PoisonError<T>) -> Self {
121        // A poison error is an internal reductstore error
122        ReductError {
123            status: ErrorCode::InternalServerError,
124            message: "Poison error".to_string(),
125        }
126    }
127}
128
129impl From<Box<dyn std::any::Any + Send>> for ReductError {
130    fn from(err: Box<dyn std::any::Any + Send>) -> Self {
131        // A box error is an internal reductstore error
132        ReductError {
133            status: ErrorCode::InternalServerError,
134            message: format!("{:?}", err),
135        }
136    }
137}
138
139impl Error for ReductError {
140    fn description(&self) -> &str {
141        &self.message
142    }
143}
144
145impl ReductError {
146    pub fn new(status: ErrorCode, message: &str) -> Self {
147        ReductError {
148            status,
149            message: message.to_string(),
150        }
151    }
152
153    pub fn status(&self) -> ErrorCode {
154        self.status
155    }
156
157    pub fn message(&self) -> &str {
158        &self.message
159    }
160
161    pub fn ok() -> ReductError {
162        ReductError {
163            status: ErrorCode::OK,
164            message: "".to_string(),
165        }
166    }
167
168    /// Create a no content error.
169    pub fn no_content(msg: &str) -> ReductError {
170        ReductError {
171            status: ErrorCode::NoContent,
172            message: msg.to_string(),
173        }
174    }
175
176    /// Create a not found error.
177    pub fn not_found(msg: &str) -> ReductError {
178        ReductError {
179            status: ErrorCode::NotFound,
180            message: msg.to_string(),
181        }
182    }
183
184    /// Create a conflict error.
185    pub fn conflict(msg: &str) -> ReductError {
186        ReductError {
187            status: ErrorCode::Conflict,
188            message: msg.to_string(),
189        }
190    }
191
192    /// Create a bad request error.
193    pub fn bad_request(msg: &str) -> ReductError {
194        ReductError {
195            status: ErrorCode::BadRequest,
196            message: msg.to_string(),
197        }
198    }
199
200    /// Create an unauthorized error.
201    pub fn unauthorized(msg: &str) -> ReductError {
202        ReductError {
203            status: ErrorCode::Unauthorized,
204            message: msg.to_string(),
205        }
206    }
207
208    /// Create a forbidden error.
209    pub fn forbidden(msg: &str) -> ReductError {
210        ReductError {
211            status: ErrorCode::Forbidden,
212            message: msg.to_string(),
213        }
214    }
215
216    /// Create an unprocessable entity error.
217    pub fn unprocessable_entity(msg: &str) -> ReductError {
218        ReductError {
219            status: ErrorCode::UnprocessableEntity,
220            message: msg.to_string(),
221        }
222    }
223
224    /// Create a too early error.
225    pub fn too_early(msg: &str) -> ReductError {
226        ReductError {
227            status: ErrorCode::TooEarly,
228            message: msg.to_string(),
229        }
230    }
231
232    /// Create a bad request error.
233    pub fn internal_server_error(msg: &str) -> ReductError {
234        ReductError {
235            status: ErrorCode::InternalServerError,
236            message: msg.to_string(),
237        }
238    }
239}
240
241// Macros for creating errors with a message.
242#[macro_export]
243macro_rules! no_content {
244    ($msg:expr, $($arg:tt)*) => {
245        ReductError::no_content(&format!($msg, $($arg)*))
246    };
247    ($msg:expr) => {
248        ReductError::no_content($msg)
249    };
250}
251
252#[macro_export]
253macro_rules! bad_request {
254    ($msg:expr, $($arg:tt)*) => {
255        ReductError::bad_request(&format!($msg, $($arg)*))
256    };
257    ($msg:expr) => {
258        ReductError::bad_request($msg)
259    };
260}
261
262#[macro_export]
263macro_rules! unprocessable_entity {
264    ($msg:expr, $($arg:tt)*) => {
265        ReductError::unprocessable_entity(&format!($msg, $($arg)*))
266    };
267    ($msg:expr) => {
268        ReductError::unprocessable_entity($msg)
269    };
270}
271
272#[macro_export]
273macro_rules! not_found {
274    ($msg:expr, $($arg:tt)*) => {
275        ReductError::not_found(&format!($msg, $($arg)*))
276    };
277    ($msg:expr) => {
278        ReductError::not_found($msg)
279    };
280}
281#[macro_export]
282macro_rules! conflict {
283    ($msg:expr, $($arg:tt)*) => {
284        ReductError::conflict(&format!($msg, $($arg)*))
285    };
286    ($msg:expr) => {
287        ReductError::conflict($msg)
288    };
289}
290
291#[macro_export]
292macro_rules! too_early {
293    ($msg:expr, $($arg:tt)*) => {
294        ReductError::too_early(&format!($msg, $($arg)*))
295    };
296    ($msg:expr) => {
297        ReductError::too_early($msg)
298    };
299}
300
301#[macro_export]
302macro_rules! internal_server_error {
303    ($msg:expr, $($arg:tt)*) => {
304        ReductError::internal_server_error(&format!($msg, $($arg)*))
305    };
306    ($msg:expr) => {
307        ReductError::internal_server_error($msg)
308    };
309}
310
311#[macro_export]
312macro_rules! unauthorized {
313    ($msg:expr, $($arg:tt)*) => {
314        ReductError::unauthorized(&format!($msg, $($arg)*))
315    };
316    ($msg:expr) => {
317        ReductError::unauthorized($msg)
318    };
319}