rs_maps/src/error.rs
2026-08-03 13:41:35 +01:00

66 lines
1.9 KiB
Rust

// src/error.rs
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::Json;
use serde_json::json;
#[derive(Debug, thiserror::Error)]
pub enum AppError {
#[error("unauthorised")]
Unauthorised,
#[error("not found")]
NotFound,
#[error("{0}")]
BadRequest(String),
#[error("{0}")]
Conflict(String),
#[error("payload too large")]
TooLarge,
/// An upstream service we proxy to (currently brouter.de) failed or was
/// unreachable. Distinct from Internal so the user is told it's not their
/// request that's at fault.
#[error("{0}")]
BadGateway(String),
#[error(transparent)]
Database(#[from] sqlx::Error),
#[error(transparent)]
Session(#[from] tower_sessions::session::Error),
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
Other(#[from] anyhow::Error),
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, message) = match &self {
AppError::Unauthorised => (StatusCode::UNAUTHORIZED, self.to_string()),
AppError::NotFound => (StatusCode::NOT_FOUND, self.to_string()),
AppError::BadRequest(m) => (StatusCode::BAD_REQUEST, m.clone()),
AppError::Conflict(m) => (StatusCode::CONFLICT, m.clone()),
AppError::TooLarge => (StatusCode::PAYLOAD_TOO_LARGE, self.to_string()),
AppError::BadGateway(m) => (StatusCode::BAD_GATEWAY, m.clone()),
other => {
// Internal detail stays in the log, never in the response body.
tracing::error!(error = %other, "internal error");
(
StatusCode::INTERNAL_SERVER_ERROR,
"something went wrong".to_string(),
)
}
};
(status, Json(json!({ "error": message }))).into_response()
}
}
pub type AppResult<T> = Result<T, AppError>;