1use axum::http::StatusCode;
2use axum::response::{IntoResponse, Response};
3use axum::Json;
4use serde::Serialize;
5use thiserror::Error;
6
7#[derive(Error, Debug)]
8pub enum ApiError {
9 #[error("entity not found: {0}")]
10 NotFound(String),
11 #[error("bad request: {0}")]
12 BadRequest(String),
13 #[error("unauthorized")]
14 Unauthorized,
15 #[error("service not registered: {domain}.{service}")]
16 ServiceNotRegistered { domain: String, service: String },
17 #[error("service unavailable: {0}")]
18 Unavailable(String),
19 #[error("internal error: {0}")]
20 Internal(String),
21}
22
23pub type ApiResult<T> = Result<T, ApiError>;
24
25#[derive(Serialize)]
26struct ErrorPayload {
27 message: String,
28}
29
30impl IntoResponse for ApiError {
31 fn into_response(self) -> Response {
32 let (status, message) = match &self {
33 Self::NotFound(_) => (StatusCode::NOT_FOUND, self.to_string()),
34 Self::BadRequest(_) => (StatusCode::BAD_REQUEST, self.to_string()),
35 Self::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()),
36 Self::ServiceNotRegistered { .. } => (StatusCode::BAD_REQUEST, self.to_string()),
37 Self::Unavailable(_) => (StatusCode::SERVICE_UNAVAILABLE, self.to_string()),
38 Self::Internal(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()),
39 };
40 (status, Json(ErrorPayload { message })).into_response()
41 }
42}