shoutdotdev/src/app_error.rs

81 lines
2.8 KiB
Rust

use std::fmt::{self, Display};
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use validator::ValidationErrors;
/// 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 AppError {
pub fn from_validation_errors(errs: ValidationErrors) -> Self {
// TODO: customize validation errors formatting
Self::BadRequest(serde_json::to_string(&errs).unwrap_or("validation error".to_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
// overwhelm server logs
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()
}
}
}
}
// Easily convert semi-arbitrary errors to InternalServerError
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) => {
write!(f, "ForbiddenError: {}", client_message)
}
AppError::NotFound(client_message) => {
write!(f, "NotFoundError: {}", client_message)
}
AppError::BadRequest(client_message) => {
write!(f, "BadRequestError: {}", client_message)
}
AppError::TooManyRequests(client_message) => {
write!(f, "TooManyRequestsError: {}", client_message)
}
}
}
}