use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("not found")] NotFound, #[error("bad request: {0}")] BadRequest(String), #[error(transparent)] Db(#[from] sqlx::Error), #[error(transparent)] Other(#[from] anyhow::Error), } impl IntoResponse for Error { fn into_response(self) -> Response { // Client errors carry their (already user-facing) message; server errors // are logged in full but return a generic body so internal detail (SQL // text, anyhow chains, file paths) never leaks to the client. let (status, message) = match &self { Error::NotFound => (StatusCode::NOT_FOUND, "not found".to_string()), Error::BadRequest(m) => (StatusCode::BAD_REQUEST, m.clone()), Error::Db(e) => { tracing::error!(error = %e, "internal db error"); ( StatusCode::INTERNAL_SERVER_ERROR, "internal server error".to_string(), ) } Error::Other(e) => { tracing::error!(error = format!("{e:#}"), "internal error"); ( StatusCode::INTERNAL_SERVER_ERROR, "internal server error".to_string(), ) } }; (status, axum::Json(serde_json::json!({ "error": message }))).into_response() } } pub type Result = std::result::Result;