1use axum::{
7 Json,
8 http::StatusCode,
9 response::{IntoResponse, Response},
10};
11use iota_storage::http_key_value_store::ItemType;
12use serde::{Deserialize, Serialize};
13use thiserror::Error;
14
15#[derive(Error, Debug)]
18pub enum ApiError {
19 #[error("bad request: {0}")]
20 BadRequest(String),
21 #[error("not found")]
22 NotFound,
23 #[error("internal server error")]
24 InternalServerError(#[from] anyhow::Error),
25}
26
27impl IntoResponse for ApiError {
28 fn into_response(self) -> Response {
29 let status_code = match self {
30 ApiError::BadRequest(_) => StatusCode::BAD_REQUEST,
31 ApiError::NotFound => StatusCode::NOT_FOUND,
32 ApiError::InternalServerError(_) => StatusCode::INTERNAL_SERVER_ERROR,
33 };
34
35 match self {
36 ApiError::InternalServerError(ref e) => {
37 tracing::Span::current().record("error", format_args!("{e:#}"));
38 }
39 ApiError::BadRequest(ref e) => {
40 tracing::Span::current().record("error", format_args!("{e}"));
41 }
42 ApiError::NotFound => {}
43 }
44
45 let body = Json(ErrorResponse {
46 error_code: status_code.as_u16().to_string(),
47 error_message: self.to_string(),
48 });
49
50 (status_code, body).into_response()
51 }
52}
53
54#[derive(Clone, Debug, Serialize, Deserialize)]
56pub(crate) struct ErrorResponse {
57 pub(crate) error_code: String,
58 pub(crate) error_message: String,
59}
60
61#[derive(Error, Debug)]
62pub enum RangeKeyBoundError {
63 #[error("expected `{expected}` item type: {detail}")]
64 UnexpectedItemType { expected: ItemType, detail: String },
65}
66
67impl From<RangeKeyBoundError> for ApiError {
68 fn from(err: RangeKeyBoundError) -> Self {
69 ApiError::BadRequest(err.to_string())
70 }
71}