enigma_relay/
error.rs

1use serde::Serialize;
2use thiserror::Error;
3
4pub type Result<T> = std::result::Result<T, EnigmaRelayError>;
5
6#[derive(Debug, Serialize, Clone)]
7pub struct ErrorBody {
8    pub code: &'static str,
9    pub message: String,
10    #[serde(skip_serializing_if = "Option::is_none")]
11    pub details: Option<serde_json::Value>,
12}
13
14#[derive(Debug, Serialize, Clone)]
15pub struct ErrorResponse {
16    pub error: ErrorBody,
17}
18
19#[derive(Debug, Error, Clone)]
20pub enum EnigmaRelayError {
21    #[error("invalid input: {0}")]
22    InvalidInput(String),
23    #[error("rate limited")]
24    RateLimited,
25    #[error("duplicate")]
26    Duplicate,
27    #[error("quota exceeded")]
28    QuotaExceeded,
29    #[error("not found")]
30    NotFound,
31    #[error("storage error: {0}")]
32    StorageError(String),
33    #[error("config error: {0}")]
34    Config(String),
35    #[error("tls error: {0}")]
36    Tls(String),
37    #[error("feature disabled: {0}")]
38    Disabled(String),
39    #[error("internal error: {0}")]
40    Internal(String),
41}
42
43impl EnigmaRelayError {
44    pub fn into_response(self) -> ErrorResponse {
45        match self.clone() {
46            EnigmaRelayError::InvalidInput(msg) => ErrorResponse {
47                error: ErrorBody {
48                    code: "invalid_input",
49                    message: msg,
50                    details: None,
51                },
52            },
53            EnigmaRelayError::RateLimited => ErrorResponse {
54                error: ErrorBody {
55                    code: "rate_limited",
56                    message: "rate limit exceeded".to_string(),
57                    details: None,
58                },
59            },
60            EnigmaRelayError::Duplicate => ErrorResponse {
61                error: ErrorBody {
62                    code: "duplicate",
63                    message: "duplicate message".to_string(),
64                    details: None,
65                },
66            },
67            EnigmaRelayError::QuotaExceeded => ErrorResponse {
68                error: ErrorBody {
69                    code: "quota_exceeded",
70                    message: "recipient quota exceeded".to_string(),
71                    details: None,
72                },
73            },
74            EnigmaRelayError::NotFound => ErrorResponse {
75                error: ErrorBody {
76                    code: "not_found",
77                    message: "not found".to_string(),
78                    details: None,
79                },
80            },
81            EnigmaRelayError::StorageError(msg) => ErrorResponse {
82                error: ErrorBody {
83                    code: "storage_error",
84                    message: msg,
85                    details: None,
86                },
87            },
88            EnigmaRelayError::Config(msg) => ErrorResponse {
89                error: ErrorBody {
90                    code: "config_error",
91                    message: msg,
92                    details: None,
93                },
94            },
95            EnigmaRelayError::Tls(msg) => ErrorResponse {
96                error: ErrorBody {
97                    code: "tls_error",
98                    message: msg,
99                    details: None,
100                },
101            },
102            EnigmaRelayError::Disabled(msg) => ErrorResponse {
103                error: ErrorBody {
104                    code: "feature_disabled",
105                    message: msg,
106                    details: None,
107                },
108            },
109            EnigmaRelayError::Internal(msg) => ErrorResponse {
110                error: ErrorBody {
111                    code: "internal_error",
112                    message: msg,
113                    details: None,
114                },
115            },
116        }
117    }
118}
119
120impl From<serde_json::Error> for EnigmaRelayError {
121    fn from(err: serde_json::Error) -> Self {
122        EnigmaRelayError::Internal(err.to_string())
123    }
124}
125
126impl From<std::io::Error> for EnigmaRelayError {
127    fn from(err: std::io::Error) -> Self {
128        EnigmaRelayError::Internal(err.to_string())
129    }
130}
131
132#[cfg(feature = "persistence")]
133impl From<sled::Error> for EnigmaRelayError {
134    fn from(err: sled::Error) -> Self {
135        EnigmaRelayError::StorageError(err.to_string())
136    }
137}
138
139#[cfg(feature = "http")]
140impl actix_web::ResponseError for EnigmaRelayError {
141    fn status_code(&self) -> actix_web::http::StatusCode {
142        match self {
143            EnigmaRelayError::InvalidInput(_) => actix_web::http::StatusCode::BAD_REQUEST,
144            EnigmaRelayError::RateLimited => actix_web::http::StatusCode::TOO_MANY_REQUESTS,
145            EnigmaRelayError::Duplicate => actix_web::http::StatusCode::OK,
146            EnigmaRelayError::QuotaExceeded => actix_web::http::StatusCode::CONFLICT,
147            EnigmaRelayError::NotFound => actix_web::http::StatusCode::NOT_FOUND,
148            EnigmaRelayError::StorageError(_) => actix_web::http::StatusCode::SERVICE_UNAVAILABLE,
149            EnigmaRelayError::Config(_) => actix_web::http::StatusCode::BAD_REQUEST,
150            EnigmaRelayError::Tls(_) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
151            EnigmaRelayError::Disabled(_) => actix_web::http::StatusCode::SERVICE_UNAVAILABLE,
152            EnigmaRelayError::Internal(_) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
153        }
154    }
155
156    fn error_response(&self) -> actix_web::HttpResponse {
157        let body = self.clone().into_response();
158        actix_web::HttpResponse::build(self.status_code()).json(body)
159    }
160}