Skip to main content

iota_rest_kv/
errors.rs

1// Copyright (c) 2025 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4//! This module includes the error types the REST API sends back to the client.
5
6use 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/// An Error type which represents the possible errors the REST API server can
16/// send back to the client.
17#[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/// Describes the response body of a unsuccessful HTTP request.
55#[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}