1use thiserror::Error;
2
3#[derive(Debug, Error)]
5pub enum BootError {
6 #[error("module name cannot be empty")]
7 EmptyModuleName,
8 #[error("route path must start with '/': {0}")]
9 InvalidRoutePath(String),
10 #[error("host pattern is invalid: {0}")]
11 InvalidHostPattern(String),
12 #[error("route is already registered: {0}")]
13 DuplicateRoute(String),
14 #[error("route was not found: {0}")]
15 NotFound(String),
16 #[error("method is not allowed: {0}")]
17 MethodNotAllowed(String),
18 #[error("provider token is already registered: {0}")]
19 DuplicateProvider(String),
20 #[error("provider token is not registered: {0}")]
21 MissingProvider(String),
22 #[error("provider token has a different concrete type: {0}")]
23 ProviderTypeMismatch(String),
24 #[error("http exception {status}: {message}")]
25 HttpException { status: u16, message: String },
26 #[error("request was forbidden: {0}")]
27 Forbidden(String),
28 #[error("request was unauthorized: {0}")]
29 Unauthorized(String),
30 #[error("bad request: {0}")]
31 BadRequest(String),
32 #[error("request timed out: {0}")]
33 RequestTimeout(String),
34 #[error("resource conflict: {0}")]
35 Conflict(String),
36 #[error("resource is gone: {0}")]
37 Gone(String),
38 #[error("precondition failed: {0}")]
39 PreconditionFailed(String),
40 #[error("payload is too large: {0}")]
41 PayloadTooLarge(String),
42 #[error("unsupported media type: {0}")]
43 UnsupportedMediaType(String),
44 #[error("not acceptable: {0}")]
45 NotAcceptable(String),
46 #[error("I am a teapot: {0}")]
47 ImATeapot(String),
48 #[error("unprocessable entity: {0}")]
49 UnprocessableEntity(String),
50 #[error("too many requests: {0}")]
51 TooManyRequests(String),
52 #[error("internal server error: {0}")]
53 InternalServerError(String),
54 #[error("not implemented: {0}")]
55 NotImplemented(String),
56 #[error("bad gateway: {0}")]
57 BadGateway(String),
58 #[error("service unavailable: {0}")]
59 ServiceUnavailable(String),
60 #[error("gateway timeout: {0}")]
61 GatewayTimeout(String),
62 #[error("adapter error: {0}")]
63 Adapter(String),
64 #[error("internal error: {0}")]
65 Internal(String),
66 #[error("I/O error: {0}")]
67 Io(#[from] std::io::Error),
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
72pub enum BootErrorKind {
73 EmptyModuleName,
74 InvalidRoutePath,
75 InvalidHostPattern,
76 DuplicateRoute,
77 NotFound,
78 MethodNotAllowed,
79 DuplicateProvider,
80 MissingProvider,
81 ProviderTypeMismatch,
82 HttpException,
83 Forbidden,
84 Unauthorized,
85 BadRequest,
86 RequestTimeout,
87 Conflict,
88 Gone,
89 PreconditionFailed,
90 PayloadTooLarge,
91 UnsupportedMediaType,
92 NotAcceptable,
93 ImATeapot,
94 UnprocessableEntity,
95 TooManyRequests,
96 InternalServerError,
97 NotImplemented,
98 BadGateway,
99 ServiceUnavailable,
100 GatewayTimeout,
101 Adapter,
102 Internal,
103 Io,
104}
105
106impl BootError {
107 pub fn kind(&self) -> BootErrorKind {
109 match self {
110 Self::EmptyModuleName => BootErrorKind::EmptyModuleName,
111 Self::InvalidRoutePath(_) => BootErrorKind::InvalidRoutePath,
112 Self::InvalidHostPattern(_) => BootErrorKind::InvalidHostPattern,
113 Self::DuplicateRoute(_) => BootErrorKind::DuplicateRoute,
114 Self::NotFound(_) => BootErrorKind::NotFound,
115 Self::MethodNotAllowed(_) => BootErrorKind::MethodNotAllowed,
116 Self::DuplicateProvider(_) => BootErrorKind::DuplicateProvider,
117 Self::MissingProvider(_) => BootErrorKind::MissingProvider,
118 Self::ProviderTypeMismatch(_) => BootErrorKind::ProviderTypeMismatch,
119 Self::HttpException { .. } => BootErrorKind::HttpException,
120 Self::Forbidden(_) => BootErrorKind::Forbidden,
121 Self::Unauthorized(_) => BootErrorKind::Unauthorized,
122 Self::BadRequest(_) => BootErrorKind::BadRequest,
123 Self::RequestTimeout(_) => BootErrorKind::RequestTimeout,
124 Self::Conflict(_) => BootErrorKind::Conflict,
125 Self::Gone(_) => BootErrorKind::Gone,
126 Self::PreconditionFailed(_) => BootErrorKind::PreconditionFailed,
127 Self::PayloadTooLarge(_) => BootErrorKind::PayloadTooLarge,
128 Self::UnsupportedMediaType(_) => BootErrorKind::UnsupportedMediaType,
129 Self::NotAcceptable(_) => BootErrorKind::NotAcceptable,
130 Self::ImATeapot(_) => BootErrorKind::ImATeapot,
131 Self::UnprocessableEntity(_) => BootErrorKind::UnprocessableEntity,
132 Self::TooManyRequests(_) => BootErrorKind::TooManyRequests,
133 Self::InternalServerError(_) => BootErrorKind::InternalServerError,
134 Self::NotImplemented(_) => BootErrorKind::NotImplemented,
135 Self::BadGateway(_) => BootErrorKind::BadGateway,
136 Self::ServiceUnavailable(_) => BootErrorKind::ServiceUnavailable,
137 Self::GatewayTimeout(_) => BootErrorKind::GatewayTimeout,
138 Self::Adapter(_) => BootErrorKind::Adapter,
139 Self::Internal(_) => BootErrorKind::Internal,
140 Self::Io(_) => BootErrorKind::Io,
141 }
142 }
143
144 pub fn http_status_code(&self) -> u16 {
146 match self {
147 Self::HttpException { status, .. } => *status,
148 Self::NotFound(_) => 404,
149 Self::MethodNotAllowed(_) => 405,
150 Self::Unauthorized(_) => 401,
151 Self::Forbidden(_) => 403,
152 Self::BadRequest(_) => 400,
153 Self::RequestTimeout(_) => 408,
154 Self::Conflict(_) => 409,
155 Self::Gone(_) => 410,
156 Self::PreconditionFailed(_) => 412,
157 Self::PayloadTooLarge(_) => 413,
158 Self::UnsupportedMediaType(_) => 415,
159 Self::NotAcceptable(_) => 406,
160 Self::ImATeapot(_) => 418,
161 Self::UnprocessableEntity(_) => 422,
162 Self::TooManyRequests(_) => 429,
163 Self::InternalServerError(_) => 500,
164 Self::NotImplemented(_) => 501,
165 Self::BadGateway(_) => 502,
166 Self::ServiceUnavailable(_) => 503,
167 Self::GatewayTimeout(_) => 504,
168 _ => 500,
169 }
170 }
171
172 pub fn http_response_message(&self) -> String {
174 match self {
175 Self::HttpException { message, .. }
176 | Self::RequestTimeout(message)
177 | Self::Conflict(message)
178 | Self::Gone(message)
179 | Self::PreconditionFailed(message)
180 | Self::ImATeapot(message)
181 | Self::UnprocessableEntity(message)
182 | Self::InternalServerError(message)
183 | Self::NotImplemented(message)
184 | Self::BadGateway(message)
185 | Self::ServiceUnavailable(message)
186 | Self::GatewayTimeout(message)
187 | Self::TooManyRequests(message)
188 | Self::NotAcceptable(message)
189 | Self::UnsupportedMediaType(message)
190 | Self::PayloadTooLarge(message)
191 | Self::BadRequest(message)
192 | Self::Forbidden(message)
193 | Self::Unauthorized(message)
194 | Self::MethodNotAllowed(message)
195 | Self::NotFound(message) => message.clone(),
196 error => error.to_string(),
197 }
198 }
199
200 pub fn http_exception(status: u16, message: impl Into<String>) -> crate::Result<Self> {
201 if (100..600).contains(&status) {
202 return Ok(Self::from_http_status(status, message));
203 }
204
205 Err(Self::Internal(format!(
206 "invalid HTTP exception status {status}"
207 )))
208 }
209
210 pub fn from_http_status(status: u16, message: impl Into<String>) -> Self {
211 let message = message.into();
212 match status {
213 400 => Self::BadRequest(message),
214 401 => Self::Unauthorized(message),
215 403 => Self::Forbidden(message),
216 404 => Self::NotFound(message),
217 405 => Self::MethodNotAllowed(message),
218 406 => Self::NotAcceptable(message),
219 408 => Self::RequestTimeout(message),
220 409 => Self::Conflict(message),
221 410 => Self::Gone(message),
222 412 => Self::PreconditionFailed(message),
223 413 => Self::PayloadTooLarge(message),
224 415 => Self::UnsupportedMediaType(message),
225 418 => Self::ImATeapot(message),
226 422 => Self::UnprocessableEntity(message),
227 429 => Self::TooManyRequests(message),
228 500 => Self::InternalServerError(message),
229 501 => Self::NotImplemented(message),
230 502 => Self::BadGateway(message),
231 503 => Self::ServiceUnavailable(message),
232 504 => Self::GatewayTimeout(message),
233 status => Self::HttpException { status, message },
234 }
235 }
236
237 pub fn not_found(message: impl Into<String>) -> Self {
238 Self::NotFound(message.into())
239 }
240
241 pub fn method_not_allowed(message: impl Into<String>) -> Self {
242 Self::MethodNotAllowed(message.into())
243 }
244
245 pub fn unauthorized(message: impl Into<String>) -> Self {
246 Self::Unauthorized(message.into())
247 }
248
249 pub fn forbidden(message: impl Into<String>) -> Self {
250 Self::Forbidden(message.into())
251 }
252
253 pub fn bad_request(message: impl Into<String>) -> Self {
254 Self::BadRequest(message.into())
255 }
256
257 pub fn request_timeout(message: impl Into<String>) -> Self {
258 Self::RequestTimeout(message.into())
259 }
260
261 pub fn conflict(message: impl Into<String>) -> Self {
262 Self::Conflict(message.into())
263 }
264
265 pub fn gone(message: impl Into<String>) -> Self {
266 Self::Gone(message.into())
267 }
268
269 pub fn precondition_failed(message: impl Into<String>) -> Self {
270 Self::PreconditionFailed(message.into())
271 }
272
273 pub fn payload_too_large(message: impl Into<String>) -> Self {
274 Self::PayloadTooLarge(message.into())
275 }
276
277 pub fn unsupported_media_type(message: impl Into<String>) -> Self {
278 Self::UnsupportedMediaType(message.into())
279 }
280
281 pub fn not_acceptable(message: impl Into<String>) -> Self {
282 Self::NotAcceptable(message.into())
283 }
284
285 pub fn im_a_teapot(message: impl Into<String>) -> Self {
286 Self::ImATeapot(message.into())
287 }
288
289 pub fn unprocessable_entity(message: impl Into<String>) -> Self {
290 Self::UnprocessableEntity(message.into())
291 }
292
293 pub fn too_many_requests(message: impl Into<String>) -> Self {
294 Self::TooManyRequests(message.into())
295 }
296
297 pub fn internal_server_error(message: impl Into<String>) -> Self {
298 Self::InternalServerError(message.into())
299 }
300
301 pub fn not_implemented(message: impl Into<String>) -> Self {
302 Self::NotImplemented(message.into())
303 }
304
305 pub fn bad_gateway(message: impl Into<String>) -> Self {
306 Self::BadGateway(message.into())
307 }
308
309 pub fn service_unavailable(message: impl Into<String>) -> Self {
310 Self::ServiceUnavailable(message.into())
311 }
312
313 pub fn gateway_timeout(message: impl Into<String>) -> Self {
314 Self::GatewayTimeout(message.into())
315 }
316
317 pub fn internal(message: impl Into<String>) -> Self {
318 Self::Internal(message.into())
319 }
320
321 pub(crate) fn clone_for_filter(&self) -> Self {
322 match self {
323 Self::HttpException { status, message } => Self::HttpException {
324 status: *status,
325 message: message.clone(),
326 },
327 Self::NotFound(message)
328 | Self::MethodNotAllowed(message)
329 | Self::Forbidden(message)
330 | Self::Unauthorized(message)
331 | Self::BadRequest(message)
332 | Self::RequestTimeout(message)
333 | Self::Conflict(message)
334 | Self::Gone(message)
335 | Self::PreconditionFailed(message)
336 | Self::PayloadTooLarge(message)
337 | Self::UnsupportedMediaType(message)
338 | Self::NotAcceptable(message)
339 | Self::ImATeapot(message)
340 | Self::UnprocessableEntity(message)
341 | Self::TooManyRequests(message)
342 | Self::InternalServerError(message)
343 | Self::NotImplemented(message)
344 | Self::BadGateway(message)
345 | Self::ServiceUnavailable(message)
346 | Self::GatewayTimeout(message)
347 | Self::Adapter(message)
348 | Self::Internal(message)
349 | Self::InvalidRoutePath(message)
350 | Self::InvalidHostPattern(message)
351 | Self::DuplicateRoute(message)
352 | Self::DuplicateProvider(message)
353 | Self::MissingProvider(message)
354 | Self::ProviderTypeMismatch(message) => {
355 Self::from_kind_and_message(self.kind(), message)
356 }
357 Self::EmptyModuleName => Self::EmptyModuleName,
358 Self::Io(error) => Self::Io(std::io::Error::new(error.kind(), error.to_string())),
359 }
360 }
361
362 fn from_kind_and_message(kind: BootErrorKind, message: &str) -> Self {
363 match kind {
364 BootErrorKind::EmptyModuleName => Self::EmptyModuleName,
365 BootErrorKind::InvalidRoutePath => Self::InvalidRoutePath(message.to_string()),
366 BootErrorKind::InvalidHostPattern => Self::InvalidHostPattern(message.to_string()),
367 BootErrorKind::DuplicateRoute => Self::DuplicateRoute(message.to_string()),
368 BootErrorKind::NotFound => Self::NotFound(message.to_string()),
369 BootErrorKind::MethodNotAllowed => Self::MethodNotAllowed(message.to_string()),
370 BootErrorKind::DuplicateProvider => Self::DuplicateProvider(message.to_string()),
371 BootErrorKind::MissingProvider => Self::MissingProvider(message.to_string()),
372 BootErrorKind::ProviderTypeMismatch => Self::ProviderTypeMismatch(message.to_string()),
373 BootErrorKind::HttpException => Self::HttpException {
374 status: 500,
375 message: message.to_string(),
376 },
377 BootErrorKind::Forbidden => Self::Forbidden(message.to_string()),
378 BootErrorKind::Unauthorized => Self::Unauthorized(message.to_string()),
379 BootErrorKind::BadRequest => Self::BadRequest(message.to_string()),
380 BootErrorKind::RequestTimeout => Self::RequestTimeout(message.to_string()),
381 BootErrorKind::Conflict => Self::Conflict(message.to_string()),
382 BootErrorKind::Gone => Self::Gone(message.to_string()),
383 BootErrorKind::PreconditionFailed => Self::PreconditionFailed(message.to_string()),
384 BootErrorKind::PayloadTooLarge => Self::PayloadTooLarge(message.to_string()),
385 BootErrorKind::UnsupportedMediaType => Self::UnsupportedMediaType(message.to_string()),
386 BootErrorKind::NotAcceptable => Self::NotAcceptable(message.to_string()),
387 BootErrorKind::ImATeapot => Self::ImATeapot(message.to_string()),
388 BootErrorKind::UnprocessableEntity => Self::UnprocessableEntity(message.to_string()),
389 BootErrorKind::TooManyRequests => Self::TooManyRequests(message.to_string()),
390 BootErrorKind::InternalServerError => Self::InternalServerError(message.to_string()),
391 BootErrorKind::NotImplemented => Self::NotImplemented(message.to_string()),
392 BootErrorKind::BadGateway => Self::BadGateway(message.to_string()),
393 BootErrorKind::ServiceUnavailable => Self::ServiceUnavailable(message.to_string()),
394 BootErrorKind::GatewayTimeout => Self::GatewayTimeout(message.to_string()),
395 BootErrorKind::Adapter => Self::Adapter(message.to_string()),
396 BootErrorKind::Internal => Self::Internal(message.to_string()),
397 BootErrorKind::Io => Self::Io(std::io::Error::other(message.to_string())),
398 }
399 }
400}