use hyper::{header, Body, Response, StatusCode}; use serde::{Deserialize, Serialize}; use thiserror::Error; use tracing::error; #[derive(Debug, Error)] pub enum ApiError { #[error("Bad request: {0:#?}")] BadRequest(anyhow::Error), #[error("Forbidden: {0}")] Forbidden(String), #[error("Unauthorized: {0}")] Unauthorized(String), #[error("NotFound: {0}")] NotFound(anyhow::Error), #[error("Conflict: {0}")] Conflict(String), #[error("Precondition failed: {0}")] PreconditionFailed(&'static str), #[error(transparent)] InternalServerError(anyhow::Error), } impl ApiError { pub fn into_response(self) -> Response
{ match self { ApiError::BadRequest(err) => HttpErrorBody::response_from_msg_and_status( format!("{err:#?}"), // use debug printing so that we give the cause StatusCode::BAD_REQUEST, ), ApiError::Forbidden(_) => { HttpErrorBody::response_from_msg_and_status(self.to_string(), StatusCode::FORBIDDEN) } ApiError::Unauthorized(_) => HttpErrorBody::response_from_msg_and_status( self.to_string(), StatusCode::UNAUTHORIZED, ), ApiError::NotFound(_) => { HttpErrorBody::response_from_msg_and_status(self.to_string(), StatusCode::NOT_FOUND) } ApiError::Conflict(_) => { HttpErrorBody::response_from_msg_and_status(self.to_string(), StatusCode::CONFLICT) } ApiError::PreconditionFailed(_) => HttpErrorBody::response_from_msg_and_status( self.to_string(), StatusCode::PRECONDITION_FAILED, ), ApiError::InternalServerError(err) => HttpErrorBody::response_from_msg_and_status( err.to_string(), StatusCode::INTERNAL_SERVER_ERROR, ), } } } #[derive(Serialize, Deserialize)] pub struct HttpErrorBody { pub msg: String, } impl HttpErrorBody { pub fn from_msg(msg: String) -> Self { HttpErrorBody { msg } } pub fn response_from_msg_and_status(msg: String, status: StatusCode) -> Response { HttpErrorBody { msg }.to_response(status) } pub fn to_response(&self, status: StatusCode) -> Response { Response::builder() .status(status) .header(header::CONTENT_TYPE, "application/json") // we do not have nested maps with non string keys so serialization shouldn't fail .body(Body::from(serde_json::to_string(self).unwrap())) .unwrap() } } pub async fn handler(err: routerify::RouteError) -> Response { let api_error = err .downcast::