66 lines
1.9 KiB
Rust
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>;
|