2025-05-02 23:48:54 -07:00
|
|
|
use std::fmt::{self, Display};
|
|
|
|
|
|
|
|
|
|
use axum::http::StatusCode;
|
|
|
|
|
use axum::response::{IntoResponse, Response};
|
|
|
|
|
|
2025-08-09 00:14:58 -07:00
|
|
|
macro_rules! forbidden {
|
|
|
|
|
($message:literal) => {
|
2025-11-19 01:31:09 +00:00
|
|
|
crate::errors::AppError::Forbidden($message.to_owned())
|
2025-08-09 00:14:58 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
($message:literal, $($param:expr),+) => {
|
2025-11-19 01:31:09 +00:00
|
|
|
crate::errors::AppError::Forbidden(format!($message, $($param)+))
|
2025-08-09 00:14:58 -07:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-28 16:35:00 -07:00
|
|
|
macro_rules! not_found {
|
|
|
|
|
($message:literal) => {
|
2025-11-19 01:31:09 +00:00
|
|
|
crate::errors::AppError::NotFound($message.to_owned())
|
2025-05-28 16:35:00 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
($message:literal, $($param:expr),+) => {
|
2025-11-19 01:31:09 +00:00
|
|
|
crate::errors::AppError::NotFound(format!($message, $($param)+))
|
2025-05-28 16:35:00 -07:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 16:20:03 -07:00
|
|
|
macro_rules! bad_request {
|
|
|
|
|
($message:literal) => {
|
2025-11-19 01:31:09 +00:00
|
|
|
crate::errors::AppError::BadRequest(format!($message))
|
2025-07-18 16:20:03 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
($message:literal, $($param:expr),+) => {
|
2025-11-19 01:31:09 +00:00
|
|
|
crate::errors::AppError::BadRequest(format!($message, $($param)+))
|
2025-07-18 16:20:03 -07:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) use bad_request;
|
2025-08-09 00:14:58 -07:00
|
|
|
pub(crate) use forbidden;
|
2025-05-28 16:35:00 -07:00
|
|
|
pub(crate) use not_found;
|
|
|
|
|
|
2025-05-02 23:48:54 -07:00
|
|
|
/// Custom error type that maps to appropriate HTTP responses.
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub enum AppError {
|
|
|
|
|
InternalServerError(anyhow::Error),
|
|
|
|
|
Forbidden(String),
|
|
|
|
|
NotFound(String),
|
|
|
|
|
BadRequest(String),
|
|
|
|
|
TooManyRequests(String),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl IntoResponse for AppError {
|
|
|
|
|
fn into_response(self) -> Response {
|
|
|
|
|
match self {
|
|
|
|
|
Self::InternalServerError(err) => {
|
|
|
|
|
tracing::error!("Application error: {:?}", err);
|
|
|
|
|
(StatusCode::INTERNAL_SERVER_ERROR, "Something went wrong").into_response()
|
|
|
|
|
}
|
|
|
|
|
Self::Forbidden(client_message) => {
|
|
|
|
|
tracing::info!("Forbidden: {}", client_message);
|
|
|
|
|
(StatusCode::FORBIDDEN, client_message).into_response()
|
|
|
|
|
}
|
|
|
|
|
Self::NotFound(client_message) => {
|
|
|
|
|
tracing::info!("Not found: {}", client_message);
|
|
|
|
|
(StatusCode::NOT_FOUND, client_message).into_response()
|
|
|
|
|
}
|
|
|
|
|
Self::TooManyRequests(client_message) => {
|
|
|
|
|
// Debug level so that if this is from a runaway loop, it won't
|
2025-12-10 22:01:47 +00:00
|
|
|
// overwhelm production logs.
|
2025-05-02 23:48:54 -07:00
|
|
|
tracing::debug!("Too many requests: {}", client_message);
|
|
|
|
|
(StatusCode::TOO_MANY_REQUESTS, client_message).into_response()
|
|
|
|
|
}
|
|
|
|
|
Self::BadRequest(client_message) => {
|
|
|
|
|
tracing::info!("Bad user input: {}", client_message);
|
|
|
|
|
(StatusCode::BAD_REQUEST, client_message).into_response()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-10 22:01:47 +00:00
|
|
|
// Easily convert semi-arbitrary errors to InternalServerError.
|
2025-05-02 23:48:54 -07:00
|
|
|
impl<E> From<E> for AppError
|
|
|
|
|
where
|
|
|
|
|
E: Into<anyhow::Error>,
|
|
|
|
|
{
|
|
|
|
|
fn from(err: E) -> Self {
|
|
|
|
|
Self::InternalServerError(Into::<anyhow::Error>::into(err))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for AppError {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
match self {
|
|
|
|
|
AppError::InternalServerError(inner) => inner.fmt(f),
|
|
|
|
|
AppError::Forbidden(client_message) => {
|
2025-10-01 22:36:19 -07:00
|
|
|
write!(f, "ForbiddenError: {client_message}")
|
2025-05-02 23:48:54 -07:00
|
|
|
}
|
|
|
|
|
AppError::NotFound(client_message) => {
|
2025-10-01 22:36:19 -07:00
|
|
|
write!(f, "NotFoundError: {client_message}")
|
2025-05-02 23:48:54 -07:00
|
|
|
}
|
|
|
|
|
AppError::BadRequest(client_message) => {
|
2025-10-01 22:36:19 -07:00
|
|
|
write!(f, "BadRequestError: {client_message}")
|
2025-05-02 23:48:54 -07:00
|
|
|
}
|
|
|
|
|
AppError::TooManyRequests(client_message) => {
|
2025-10-01 22:36:19 -07:00
|
|
|
write!(f, "TooManyRequestsError: {client_message}")
|
2025-05-02 23:48:54 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|