apikit/
reject.rs

1//! Commonly used rejections and recovery procedures.
2use std::fmt::Display;
3
4use axum::http::StatusCode;
5
6use axum::response::{IntoResponse, Response};
7
8use miette::Diagnostic;
9use serde::Serialize;
10
11use crate::reply;
12
13const MESSAGE_NOT_FOUND: &str = "not found";
14const MESSAGE_FORBIDDEN: &str = "forbidden";
15const MESSAGE_INTERNAL_SERVER_ERROR: &str = "internal server error";
16
17#[derive(Debug, Diagnostic, Serialize)]
18#[serde(untagged)]
19pub enum HTTPError {
20    BadRequest {
21        error: String,
22    },
23    Forbidden,
24    NotFound,
25    InternalServerError {
26        error: String,
27        backtrace: Option<String>,
28    },
29}
30
31impl Display for HTTPError {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        match self {
34            Self::BadRequest { error } => write!(f, "bad request: {}", error),
35            Self::Forbidden => write!(f, "forbidden"),
36            Self::NotFound => write!(f, "not found"),
37            Self::InternalServerError {
38                error,
39                backtrace: Some(backtrace),
40            } => write!(f, "internal server error: {}\n{}", error, backtrace),
41            Self::InternalServerError {
42                error,
43                backtrace: None,
44            } => write!(f, "internal server error: {}", error),
45        }
46    }
47}
48
49impl std::error::Error for HTTPError {}
50
51impl HTTPError {
52    pub fn bad_request<S: ToString>(s: S) -> Self {
53        Self::BadRequest {
54            error: s.to_string(),
55        }
56    }
57
58    pub fn internal_server_error<E: ToString>(e: E) -> Self {
59        Self::InternalServerError {
60            error: e.to_string(),
61            backtrace: None, // TODO: Properly capture backtrace
62        }
63    }
64}
65
66impl IntoResponse for HTTPError {
67    fn into_response(self) -> Response {
68        match self {
69            Self::BadRequest { error } => reply::error(error, StatusCode::BAD_REQUEST),
70            Self::Forbidden => reply::error(MESSAGE_FORBIDDEN, StatusCode::FORBIDDEN),
71            Self::NotFound => reply::error(MESSAGE_NOT_FOUND, StatusCode::NOT_FOUND),
72            Self::InternalServerError {
73                ref error,
74                ref backtrace,
75            } => {
76                if let Some(backtrace) = backtrace {
77                    tracing::error!("{error}\n{backtrace}");
78                } else {
79                    tracing::error!("{error}");
80                }
81                reply::error(
82                    MESSAGE_INTERNAL_SERVER_ERROR,
83                    StatusCode::INTERNAL_SERVER_ERROR,
84                )
85            }
86        }
87    }
88}