objectstore_server/endpoints/
common.rs1use std::error::Error;
4
5use axum::Json;
6use axum::http::StatusCode;
7use axum::response::{IntoResponse, Response};
8use objectstore_service::ServiceError;
9use serde::{Deserialize, Serialize};
10use thiserror::Error;
11
12use crate::auth::AuthError;
13
14#[derive(Debug, Error)]
16pub enum ApiError {
17 #[error("client error: {0}")]
19 Client(String),
20
21 #[error("auth error: {0}")]
23 Auth(#[from] AuthError),
24
25 #[error("service error: {0}")]
27 Service(#[from] ServiceError),
28}
29
30pub type ApiResult<T> = Result<T, ApiError>;
32
33#[derive(Serialize, Deserialize, Debug)]
35pub struct ApiErrorResponse {
36 #[serde(default)]
38 detail: Option<String>,
39 #[serde(default, skip_serializing_if = "Vec::is_empty")]
41 causes: Vec<String>,
42}
43
44impl ApiErrorResponse {
45 pub fn from_error<E: Error + ?Sized>(error: &E) -> Self {
47 let detail = Some(error.to_string());
48
49 let mut causes = Vec::new();
50 let mut source = error.source();
51 while let Some(s) = source {
52 causes.push(s.to_string());
53 source = s.source();
54 }
55
56 Self { detail, causes }
57 }
58}
59
60impl IntoResponse for ApiError {
61 fn into_response(self) -> Response {
62 let status = match &self {
63 ApiError::Client(_) => StatusCode::BAD_REQUEST,
64
65 ApiError::Auth(AuthError::BadRequest(_)) => StatusCode::BAD_REQUEST,
66 ApiError::Auth(AuthError::ValidationFailure(_))
67 | ApiError::Auth(AuthError::VerificationFailure) => StatusCode::UNAUTHORIZED,
68 ApiError::Auth(AuthError::NotPermitted) => StatusCode::FORBIDDEN,
69 ApiError::Auth(AuthError::InternalError(_)) => {
70 tracing::error!(error = &self as &dyn Error, "auth system error");
71 StatusCode::INTERNAL_SERVER_ERROR
72 }
73
74 ApiError::Service(_) => {
75 tracing::error!(error = &self as &dyn Error, "error handling request");
76 StatusCode::INTERNAL_SERVER_ERROR
77 }
78 };
79
80 let body = ApiErrorResponse::from_error(&self);
81 (status, Json(body)).into_response()
82 }
83}