Skip to main content

crab_vault_engine/
error.rs

1use axum::{
2    http::StatusCode,
3    response::{IntoResponse, Response},
4};
5use serde::Serialize;
6use thiserror::Error;
7
8pub type EngineResult<T> = Result<T, EngineError>;
9
10#[derive(Debug, Serialize, Error)]
11#[serde(rename_all = "camelCase", tag = "code")]
12pub enum EngineError {
13    #[error("io error: {error} while manipulating {path}")]
14    Io {
15        #[serde(skip)]
16        error: std::io::Error,
17        path: String,
18    },
19
20    #[error("ser/de error: {error} at line{line}, column {column}")]
21    Serde {
22        error: &'static str,
23        line: usize,
24        column: usize,
25    },
26
27    #[error("bucket not found: {bucket}")]
28    BucketNotFound { bucket: String },
29
30    #[error("bucket meta not found: {bucket}")]
31    BucketMetaNotFound { bucket: String },
32
33    #[error("bucket not empty, possibly while deleting, details {bucket}")]
34    BucketNotEmpty { bucket: String },
35
36    #[error("object not found: {bucket}/{object}")]
37    ObjectNotFound { bucket: String, object: String },
38
39    #[error("object meta not found: {bucket}/{object}")]
40    ObjectMetaNotFound { bucket: String, object: String },
41
42    #[allow(dead_code)]
43    #[error("some other errors: {0}")]
44    Other(String),
45
46    #[allow(dead_code)]
47    #[error("backend error: {0}")]
48    BackendError(String),
49
50    #[error("invalid argument: {0}")]
51    InvalidArgument(String),
52}
53
54impl From<serde_json::error::Error> for EngineError {
55    fn from(value: serde_json::error::Error) -> Self {
56        use serde_json::error::Category;
57        let kind = match value.classify() {
58            Category::Io => "io",
59            Category::Syntax => "syntax",
60            Category::Data => "data",
61            Category::Eof => "eof",
62        };
63
64        EngineError::Serde {
65            error: kind,
66            line: value.line(),
67            column: value.column(),
68        }
69    }
70}
71
72impl IntoResponse for EngineError {
73    fn into_response(self) -> Response {
74        use EngineError::*;
75        let code = match &self {
76            Serde {
77                error: _,
78                line: _,
79                column: _,
80            }
81            | Io { error: _, path: _ }
82            | BackendError(_)
83            | Other(_) => StatusCode::INTERNAL_SERVER_ERROR,
84
85            ObjectNotFound {
86                bucket: _,
87                object: _,
88            }
89            | BucketNotFound { bucket: _ } => StatusCode::NOT_FOUND,
90
91            ObjectMetaNotFound {
92                bucket: _,
93                object: _,
94            }
95            | BucketMetaNotFound { bucket: _ } => StatusCode::NOT_FOUND,
96
97            BucketNotEmpty { bucket: _ } => StatusCode::CONFLICT,
98            InvalidArgument(_) => StatusCode::UNPROCESSABLE_ENTITY,
99        };
100
101        #[derive(Serialize)]
102        struct Msg {
103            #[serde(flatten)]
104            error: EngineError,
105            msg: String,
106        }
107
108        (
109            code,
110            axum::Json(Msg {
111                msg: self.to_string(),
112                error: self,
113            }),
114        )
115            .into_response()
116    }
117}
118
119impl From<EngineError> for Response {
120    fn from(value: EngineError) -> Self {
121        value.into_response()
122    }
123}