1use std::net::AddrParseError;
2
3use config::ConfigError;
4use fusion_common::ctx::CtxError;
5use serde::Serialize;
6use serde_json::json;
7
8use crate::{configuration::ConfigureError, security::Error as SecurityError};
9
10#[derive(Debug, Serialize)]
11pub struct DataError {
12 pub code: i32,
13 pub msg: String,
14 #[serde(skip_serializing_if = "Option::is_none")]
15 pub data: Option<serde_json::Value>,
16 #[serde(skip)]
17 pub source: Option<Box<dyn core::error::Error + Send + Sync>>,
18}
19
20impl fusion_common::DataError for DataError {
21 fn code(&self) -> i32 {
22 self.code
23 }
24
25 fn msg(&self) -> &str {
26 &self.msg
27 }
28
29 fn data(&self) -> Option<&serde_json::Value> {
30 self.data.as_ref()
31 }
32
33 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
34 self.source.as_ref().map(|e| &**e as &(dyn core::error::Error + 'static))
35 }
36}
37
38impl core::error::Error for DataError {
39 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
40 self.source.as_ref().map(|e| &**e as &(dyn core::error::Error + 'static))
41 }
42}
43
44impl core::fmt::Display for DataError {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 write!(f, "{}:{}", self.code, self.msg)
47 }
48}
49
50impl DataError {
51 pub fn bad_request(msg: impl Into<String>) -> Self {
52 Self { code: 400, msg: msg.into(), data: None, source: None }
53 }
54
55 pub fn not_found(msg: impl Into<String>) -> Self {
56 Self { code: 404, msg: msg.into(), data: None, source: None }
57 }
58
59 pub fn conflicted(msg: impl Into<String>) -> Self {
60 Self { code: 409, msg: msg.into(), data: None, source: None }
61 }
62
63 pub fn unauthorized(msg: impl Into<String>) -> Self {
64 Self { code: 401, msg: msg.into(), data: None, source: None }
65 }
66
67 pub fn forbidden(msg: impl Into<String>) -> Self {
68 Self { code: 403, msg: msg.into(), data: None, source: None }
69 }
70
71 pub fn server_error(msg: impl Into<String>) -> Self {
72 Self { code: 500, msg: msg.into(), data: None, source: None }
73 }
74
75 pub fn biz_error(code: i32, msg: impl Into<String>, data: Option<serde_json::Value>) -> Self {
76 Self { code, msg: msg.into(), data, source: None }
77 }
78
79 pub fn internal(
80 code: i32,
81 msg: impl Into<String>,
82 source: Option<Box<dyn core::error::Error + Send + Sync>>,
83 ) -> Self {
84 Self { code, msg: msg.into(), data: None, source }
85 }
86
87 pub fn retry_limit(msg: impl Into<String>, retry_limit: u32) -> Self {
88 let detail = json!({ "retry_limit": retry_limit });
89 Self { code: 1429, msg: msg.into(), data: Some(detail), source: None }
90 }
91}
92
93impl From<fusion_common::Error> for DataError {
94 fn from(value: fusion_common::Error) -> Self {
95 DataError::server_error(value.to_string())
96 }
97}
98
99impl From<std::time::SystemTimeError> for DataError {
100 fn from(value: std::time::SystemTimeError) -> Self {
101 Self::internal(500, "SystemTimeError", Some(Box::new(value)))
102 }
103}
104
105impl From<std::io::Error> for DataError {
106 fn from(value: std::io::Error) -> Self {
107 let error_msg = value.to_string();
108 DataError::internal(500, format!("IO error: {}", error_msg), Some(Box::new(value)))
109 }
110}
111
112impl From<serde_json::Error> for DataError {
113 fn from(value: serde_json::Error) -> Self {
114 DataError::internal(500, "JSON error", Some(Box::new(value)))
115 }
116}
117
118impl<T> From<tokio::sync::mpsc::error::SendError<T>> for DataError
119where
120 T: Send + Sync + 'static,
121{
122 fn from(e: tokio::sync::mpsc::error::SendError<T>) -> Self {
123 let compatible_error: Box<dyn std::error::Error + Send + Sync + 'static> = Box::new(e);
124 DataError::internal(500, "channel send error", Some(compatible_error))
125 }
126}
127
128impl From<tokio::sync::oneshot::error::RecvError> for DataError {
129 fn from(e: tokio::sync::oneshot::error::RecvError) -> Self {
130 let compatible_error: Box<dyn std::error::Error + Send + Sync + 'static> = Box::new(e);
132 DataError::internal(500, "channel recv error", Some(compatible_error))
133 }
134}
135
136impl From<tokio::task::JoinError> for DataError {
137 fn from(value: tokio::task::JoinError) -> Self {
138 let compatible_error: Box<dyn std::error::Error + Send + Sync + 'static> = Box::new(value);
140 DataError::internal(500, "Join tokio task error", Some(compatible_error))
141 }
142}
143
144impl From<ConfigError> for DataError {
145 fn from(value: ConfigError) -> Self {
146 DataError::server_error(format!("Config load error: {}", value))
147 }
148}
149
150impl From<AddrParseError> for DataError {
151 fn from(value: AddrParseError) -> Self {
152 DataError::server_error(format!("Addr parse error: {}", value))
153 }
154}
155
156impl From<CtxError> for DataError {
157 fn from(value: CtxError) -> Self {
158 match value {
159 CtxError::Unauthorized(msg) => DataError::unauthorized(msg),
160 CtxError::InvalidPayload => DataError::unauthorized("Invalid ctx payload"),
161 }
162 }
163}
164
165#[cfg(feature = "with-uuid")]
166impl From<uuid::Error> for DataError {
167 fn from(value: uuid::Error) -> Self {
168 DataError::internal(500, value.to_string(), None)
169 }
170}
171
172impl From<ConfigureError> for DataError {
173 fn from(value: ConfigureError) -> Self {
174 DataError::server_error(value.to_string())
175 }
176}
177
178#[cfg(feature = "tonic")]
179impl From<protobuf::ParseError> for DataError {
180 fn from(value: protobuf::ParseError) -> Self {
181 DataError::biz_error(400, format!("Protobuf parse error: {}", value), None)
182 }
183}
184
185#[cfg(feature = "tonic")]
186impl From<protobuf::SerializeError> for DataError {
187 fn from(value: protobuf::SerializeError) -> Self {
188 let source: Option<Box<dyn core::error::Error + Send + Sync>> = Some(Box::new(value));
190 DataError::internal(500, "Protobuf serialize error", source)
191 }
192}
193
194#[cfg(feature = "tonic")]
195impl From<tonic::transport::Error> for DataError {
196 fn from(value: tonic::transport::Error) -> Self {
197 DataError::server_error(format!("Grpc transport error: {}", value))
198 }
199}
200
201#[cfg(feature = "tonic")]
202impl From<tonic::Status> for DataError {
203 fn from(value: tonic::Status) -> Self {
204 let msg = value.message();
206 match value.code() {
207 tonic::Code::Cancelled => DataError::server_error(msg),
208 tonic::Code::Unknown => DataError::server_error(msg),
209 tonic::Code::InvalidArgument => DataError::bad_request(msg),
210 tonic::Code::DeadlineExceeded => DataError::server_error(msg),
211 tonic::Code::NotFound => DataError::not_found(msg),
212 tonic::Code::AlreadyExists => DataError::conflicted(msg),
213 tonic::Code::PermissionDenied => DataError::server_error(msg),
214 tonic::Code::ResourceExhausted => DataError::server_error(msg),
215 tonic::Code::FailedPrecondition => DataError::forbidden(msg),
216 tonic::Code::Aborted => DataError::server_error(msg),
217 tonic::Code::OutOfRange => DataError::bad_request(msg),
218 tonic::Code::Unimplemented => DataError::server_error(msg),
219 tonic::Code::Internal => DataError::server_error(msg),
220 tonic::Code::Unavailable => DataError::server_error(msg),
221 tonic::Code::DataLoss => DataError::server_error(msg),
222 tonic::Code::Unauthenticated => DataError::unauthorized(msg),
223 tonic::Code::Ok => DataError::internal(0, "", None),
225 }
226 }
227}
228
229#[cfg(feature = "tonic")]
230impl From<DataError> for tonic::Status {
231 fn from(value: DataError) -> Self {
232 let code = match value.code {
233 400 => tonic::Code::InvalidArgument,
234 401 => tonic::Code::Unauthenticated,
235 403 => tonic::Code::PermissionDenied,
236 404 => tonic::Code::NotFound,
237 409 => tonic::Code::Aborted,
238 413 => tonic::Code::ResourceExhausted,
239 429 => tonic::Code::Unavailable,
240 500 => tonic::Code::Internal,
241 501 => tonic::Code::Unimplemented,
242 503 => tonic::Code::Unavailable,
243 504 => tonic::Code::DeadlineExceeded,
244 505 => tonic::Code::Unavailable,
245 0 | (200..=299) => tonic::Code::Ok,
246 _ => tonic::Code::Unknown,
247 };
248 let mut status = tonic::Status::new(code, value.msg);
249 if let Some(e) = value.source {
253 let arc_error = std::sync::Arc::from(e);
256 status.set_source(arc_error);
257 }
258 status
259 }
260}
261
262#[cfg(feature = "fusionsql")]
263impl From<fusionsql::SqlError> for DataError {
264 fn from(value: fusionsql::SqlError) -> Self {
265 match value {
266 fusionsql::SqlError::Unauthorized(e) => DataError::unauthorized(e),
267 fusionsql::SqlError::InvalidArgument { message } => DataError::bad_request(format!("InvalidArgument, {message}")),
268 fusionsql::SqlError::EntityNotFound { schema, entity, id } => {
269 DataError::not_found(format!("EntityNotFound, {}:{}:{}", schema.unwrap_or_default(), entity, id))
270 }
271 fusionsql::SqlError::NotFound { schema, table, sql } => {
272 log::debug!("NotFound, schema: {}, table: {}, sql: {}", schema.unwrap_or_default(), table, sql);
273 DataError::not_found(format!("NotFound, {}:{}", schema.unwrap_or_default(), table))
274 }
275 fusionsql::SqlError::ListLimitOverMax { max, actual } => {
276 DataError::bad_request(format!("ListLimitOverMax, max: {max}, actual: {actual}"))
277 }
278 fusionsql::SqlError::ListLimitUnderMin { min, actual } => {
279 DataError::bad_request(format!("ListLimitUnderMin, min: {min}, actual: {actual}"))
280 }
281 fusionsql::SqlError::ListPageUnderMin { min, actual } => {
282 DataError::bad_request(format!("ListPageUnderMin, min: {min}, actual: {actual}"))
283 }
284 fusionsql::SqlError::UserAlreadyExists { key, value } => {
285 DataError::conflicted(format!("UserAlreadyExists, {key}:{value}"))
286 }
287 fusionsql::SqlError::UniqueViolation { table, constraint } => {
288 DataError::conflicted(format!("UniqueViolation, {table}:{constraint}"))
289 }
290 fusionsql::SqlError::ExecuteError { table, message } => {
291 DataError::server_error(format!("ExecuteError, {}:{}", table, message))
292 }
293 fusionsql::SqlError::ExecuteFail { schema, table } => {
294 DataError::server_error(format!("ExecuteFail, {:?}:{}", schema, table))
295 }
296 fusionsql::SqlError::CountFail { schema, table } => {
297 DataError::server_error(format!("CountFail, {:?}:{}", schema, table))
298 }
299 e @ fusionsql::SqlError::InvalidDatabase(_) => DataError::server_error(e.to_string()),
300 e @ fusionsql::SqlError::CantCreateModelManagerProvider(_) => DataError::server_error(e.to_string()),
301 e @ fusionsql::SqlError::IntoSeaError(_) => DataError::server_error(e.to_string()),
302 e @ fusionsql::SqlError::SeaQueryError(_) => DataError::server_error(e.to_string()),
303 e @ fusionsql::SqlError::JsonError(_) => DataError::server_error(e.to_string()),
304 fusionsql::SqlError::DbxError(e) => {
305 let error_msg = e.to_string();
307 let compatible_error: Box<dyn std::error::Error + Send + Sync + 'static> =
308 Box::new(std::io::Error::other(error_msg));
309 DataError::internal(500, "Dbx Error", Some(compatible_error))
310 }
311 fusionsql::SqlError::Sqlx(e) => {
312 let error_msg = e.to_string();
314 let compatible_error: Box<dyn std::error::Error + Send + Sync + 'static> =
315 Box::new(std::io::Error::other(error_msg));
316 DataError::internal(500, "Sqlx Error", Some(compatible_error))
317 }
318 }
319 }
320}
321
322#[cfg(feature = "fusionsql")]
323impl From<fusionsql::store::DbxError> for DataError {
324 fn from(value: fusionsql::store::DbxError) -> Self {
325 DataError::server_error(value.to_string())
326 }
327}
328
329impl From<SecurityError> for DataError {
330 fn from(value: SecurityError) -> Self {
331 match value {
332 SecurityError::TokenExpired => DataError::unauthorized("Token expired"),
333 SecurityError::SignatureNotMatching => DataError::unauthorized("Signature not matching"),
334 SecurityError::InvalidPassword => DataError::unauthorized("Invalid password"),
335 SecurityError::FailedToVerifyPassword => DataError::unauthorized("Failed to verify password"),
336 _ => DataError::server_error(value.to_string()),
343 }
344 }
345}
346
347impl<T> From<mea::mpsc::SendError<T>> for DataError {
348 fn from(value: mea::mpsc::SendError<T>) -> Self {
349 DataError::server_error(format!("Send to mea::mpsc error, {}", value))
350 }
351}