1use crate::log::log_def::CODE_STR;
2use crate::log_e;
3use crate::store::db::sql_def::DbError;
4use log::error;
5use num_derive::FromPrimitive;
6use std::backtrace::Backtrace;
7use std::fmt;
8
9#[repr(i32)]
10#[derive(Clone, Copy, Debug, FromPrimitive, PartialEq)]
11pub enum VibeEngineErrorCode {
13 Unknown = -1,
14
15 Success = 200,
16
17 EngineDropped = 100001,
19
20 DatabaseNotOpened = 100002,
22
23 DatabaseOpenFailed = 100003,
25 LogDatabaseOpenFailed = 100223,
26
27 WorkDatabaseOpenFailed = 132003,
28
29 DatabaseIOError = 100004,
31
32 DatabaseTargetNotFound = 100005,
34
35 DatabaseThreadError = 100006,
37
38 PostError = 100007,
39 Cancelled = 100008,
41
42 ParameterEmpty = 100012,
43 OAuthError = 100013,
44 ConfigError = 100014,
45
46 IOError = 100017,
48 BadRequest = 100018,
49 RequestError = 100019,
50 InternalServerError = 100020,
51 NetworkError = 100021,
53 UnsupportedError = 100022,
55
56 TimeoutError = 100023,
58 ConnectError = 100024,
60 TlsConnectError = 100025,
62 OptionsParseError = 100026,
64
65 SerdeDeserializeError = 100027,
66 SerdeSerializeError = 100028,
67
68 InvalidArgumentLogInfo = 100029,
69
70 MPSCSendError = 100037,
71
72 GenerateQRError = 10005,
73 RuntimeError = 10006,
74 BeaverError = 10007,
75 SocketRecvTimeout = 10008,
76 SocketClosed = 10009,
77 ProtocParseError = 10010,
78 SocketNotOpened = 10011,
79 TaskInterruptionError = 10012,
80
81 ConnectionClosed = 30001,
83
84 ConnectionExists = 34001,
86
87 ConnectionClosing = 30027,
89
90 InternalError = 32002,
92 NotLoggedInError = 32003,
93 PageTokenError = 32004,
94 ClipboardInitializeError = 32005,
95
96 NotSupportedYet = 999999,
97}
98
99impl VibeEngineErrorCode {
100 pub fn code(&self) -> i32 {
101 *self as i32
102 }
103}
104
105#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
106pub enum VibeErrorKind {
108 Config,
109 Runtime,
110 Task,
111 Database,
112 Network,
113 Log,
114 Unsupported,
115 Internal,
116 Unknown,
117}
118
119impl VibeErrorKind {
120 fn from_code(code: VibeEngineErrorCode) -> Self {
121 match code {
122 VibeEngineErrorCode::ConfigError
123 | VibeEngineErrorCode::ParameterEmpty
124 | VibeEngineErrorCode::InvalidArgumentLogInfo => Self::Config,
125 VibeEngineErrorCode::RuntimeError
126 | VibeEngineErrorCode::EngineDropped
127 | VibeEngineErrorCode::MPSCSendError => Self::Runtime,
128 VibeEngineErrorCode::PostError
129 | VibeEngineErrorCode::TaskInterruptionError
130 | VibeEngineErrorCode::Cancelled => Self::Task,
131 VibeEngineErrorCode::DatabaseNotOpened
132 | VibeEngineErrorCode::DatabaseOpenFailed
133 | VibeEngineErrorCode::LogDatabaseOpenFailed
134 | VibeEngineErrorCode::WorkDatabaseOpenFailed
135 | VibeEngineErrorCode::DatabaseIOError
136 | VibeEngineErrorCode::DatabaseTargetNotFound
137 | VibeEngineErrorCode::DatabaseThreadError => Self::Database,
138 VibeEngineErrorCode::NetworkError
139 | VibeEngineErrorCode::BadRequest
140 | VibeEngineErrorCode::RequestError
141 | VibeEngineErrorCode::InternalServerError
142 | VibeEngineErrorCode::TimeoutError
143 | VibeEngineErrorCode::ConnectError
144 | VibeEngineErrorCode::TlsConnectError
145 | VibeEngineErrorCode::SocketRecvTimeout
146 | VibeEngineErrorCode::SocketClosed
147 | VibeEngineErrorCode::SocketNotOpened
148 | VibeEngineErrorCode::ConnectionClosed
149 | VibeEngineErrorCode::ConnectionExists
150 | VibeEngineErrorCode::ConnectionClosing => Self::Network,
151 VibeEngineErrorCode::UnsupportedError | VibeEngineErrorCode::NotSupportedYet => {
152 Self::Unsupported
153 }
154 VibeEngineErrorCode::InternalError
155 | VibeEngineErrorCode::IOError
156 | VibeEngineErrorCode::SerdeDeserializeError
157 | VibeEngineErrorCode::SerdeSerializeError
158 | VibeEngineErrorCode::OptionsParseError
159 | VibeEngineErrorCode::GenerateQRError
160 | VibeEngineErrorCode::BeaverError
161 | VibeEngineErrorCode::ProtocParseError
162 | VibeEngineErrorCode::OAuthError
163 | VibeEngineErrorCode::NotLoggedInError
164 | VibeEngineErrorCode::PageTokenError
165 | VibeEngineErrorCode::ClipboardInitializeError => Self::Internal,
166 VibeEngineErrorCode::Success | VibeEngineErrorCode::Unknown => Self::Unknown,
167 }
168 }
169}
170
171#[derive(Debug, Clone)]
172pub struct VibeEngineError {
174 code: i32,
175 kind: VibeErrorKind,
176 message: String,
177 source: Option<String>,
178 context: Vec<String>,
179}
180
181impl fmt::Display for VibeEngineError {
182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183 write!(
184 f,
185 "vibe-ready error [{:?}/{}]: {}",
186 self.kind, self.code, self.message
187 )?;
188 if let Some(source) = &self.source {
189 write!(f, "; source: {source}")?;
190 }
191 if !self.context.is_empty() {
192 write!(f, "; context: {}", self.context.join("; "))?;
193 }
194 Ok(())
195 }
196}
197
198impl std::error::Error for VibeEngineError {}
199
200impl From<DbError> for VibeEngineError {
201 fn from(err: DbError) -> Self {
202 error!("DbError: {:?}, {:?}", err, Backtrace::capture());
203 let db_error = err.clone();
204 let mut error = match err {
205 DbError::OpenFailed => {
206 VibeEngineError::from_code(VibeEngineErrorCode::DatabaseOpenFailed)
207 }
208 DbError::DatabaseIOError => {
209 VibeEngineError::from_code(VibeEngineErrorCode::DatabaseIOError)
210 }
211 DbError::NotOpen => VibeEngineError::from_code(VibeEngineErrorCode::DatabaseOpenFailed),
212 DbError::TargetNotFound => {
213 VibeEngineError::from_code(VibeEngineErrorCode::DatabaseTargetNotFound)
214 }
215 DbError::DatabaseThreadError => {
216 VibeEngineError::from_code(VibeEngineErrorCode::DatabaseThreadError)
217 }
218 DbError::DatabaseUnlockError => {
219 VibeEngineError::from_code(VibeEngineErrorCode::DatabaseOpenFailed)
220 }
221 DbError::NotSupportedYet => {
222 VibeEngineError::from_code(VibeEngineErrorCode::DatabaseIOError)
223 }
224 DbError::JoinError => VibeEngineError::from_code(VibeEngineErrorCode::DatabaseIOError),
225 _ => VibeEngineError::from_code(VibeEngineErrorCode::Unknown),
226 };
227 error.source = Some(format!("{db_error:?}"));
228 error
229 }
230}
231
232impl VibeEngineError {
233 pub fn code(&self) -> i32 {
234 self.code
235 }
236
237 pub fn kind(&self) -> VibeErrorKind {
238 self.kind
239 }
240
241 pub fn message(&self) -> &str {
242 &self.message
243 }
244
245 pub fn source_message(&self) -> Option<&str> {
246 self.source.as_deref()
247 }
248
249 pub fn context(&self) -> &[String] {
250 &self.context
251 }
252
253 pub fn with_source(mut self, source: impl Into<String>) -> Self {
254 self.source = Some(source.into());
255 self
256 }
257
258 pub fn with_context(mut self, context: impl Into<String>) -> Self {
259 self.context.push(context.into());
260 self
261 }
262
263 pub fn from_success() -> Self {
264 Self::from_error_code(VibeEngineErrorCode::Success)
265 }
266
267 pub fn from_u16(code: u16) -> Self {
268 VibeEngineError::from_raw_code(code as i32)
269 }
270
271 pub fn from_u32(code: u32) -> Self {
272 VibeEngineError::from_raw_code(code as i32)
273 }
274
275 pub fn is_success(&self) -> bool {
276 self.code == VibeEngineErrorCode::Success.code()
277 }
278
279 pub fn same_code(&self) -> Self {
280 self.clone()
281 }
282
283 pub fn from_error_code(value: VibeEngineErrorCode) -> Self {
284 VibeEngineError {
285 code: value.code(),
286 kind: VibeErrorKind::from_code(value),
287 message: default_message(value).to_string(),
288 source: None,
289 context: Vec::new(),
290 }
291 }
292
293 pub fn from_code(value: VibeEngineErrorCode) -> Self {
294 Self::from_error_code(value)
295 }
296
297 pub fn from_error_code_msg(value: VibeEngineErrorCode, msg: String) -> Self {
298 VibeEngineError {
299 message: msg,
300 ..Self::from_error_code(value)
301 }
302 }
303
304 pub fn from_task_interruption_error() -> Self {
305 Self::from_error_code(VibeEngineErrorCode::TaskInterruptionError)
306 }
307
308 pub fn from_internal_error() -> Self {
309 Self::from_error_code(VibeEngineErrorCode::InternalError)
310 }
311
312 pub fn from_mpsc_send_error() -> Self {
313 Self::from_error_code(VibeEngineErrorCode::MPSCSendError)
314 }
315
316 pub fn from_parameter_empty() -> Self {
317 Self::from_error_code(VibeEngineErrorCode::ParameterEmpty)
318 }
319 pub fn from_parameter_empty_log(tag: &str) -> Self {
320 let code = VibeEngineErrorCode::ParameterEmpty.code();
321 log_e!(tag, CODE_STR, code);
322 Self::from_error_code(VibeEngineErrorCode::ParameterEmpty)
323 }
324
325 fn from_raw_code(code: i32) -> Self {
326 VibeEngineError {
327 code,
328 kind: VibeErrorKind::Unknown,
329 message: format!("unknown error code {code}"),
330 source: None,
331 context: Vec::new(),
332 }
333 }
334}
335
336fn default_message(code: VibeEngineErrorCode) -> &'static str {
337 match code {
338 VibeEngineErrorCode::Unknown => "unknown error",
339 VibeEngineErrorCode::Success => "success",
340 VibeEngineErrorCode::EngineDropped => "engine has been dropped",
341 VibeEngineErrorCode::DatabaseNotOpened => "database is not opened",
342 VibeEngineErrorCode::DatabaseOpenFailed => "database open failed",
343 VibeEngineErrorCode::LogDatabaseOpenFailed => "log database open failed",
344 VibeEngineErrorCode::WorkDatabaseOpenFailed => "work database open failed",
345 VibeEngineErrorCode::DatabaseIOError => "database I/O error",
346 VibeEngineErrorCode::DatabaseTargetNotFound => "database target not found",
347 VibeEngineErrorCode::DatabaseThreadError => "database worker thread error",
348 VibeEngineErrorCode::PostError => "task post failed",
349 VibeEngineErrorCode::Cancelled => "task was cancelled",
350 VibeEngineErrorCode::ParameterEmpty => "required parameter is empty",
351 VibeEngineErrorCode::OAuthError => "OAuth error",
352 VibeEngineErrorCode::ConfigError => "configuration error",
353 VibeEngineErrorCode::IOError => "I/O error",
354 VibeEngineErrorCode::BadRequest => "bad request",
355 VibeEngineErrorCode::RequestError => "request error",
356 VibeEngineErrorCode::InternalServerError => "internal server error",
357 VibeEngineErrorCode::NetworkError => "network error",
358 VibeEngineErrorCode::UnsupportedError => "unsupported operation",
359 VibeEngineErrorCode::TimeoutError => "operation timed out",
360 VibeEngineErrorCode::ConnectError => "connection failed",
361 VibeEngineErrorCode::TlsConnectError => "TLS connection failed",
362 VibeEngineErrorCode::OptionsParseError => "options parse failed",
363 VibeEngineErrorCode::SerdeDeserializeError => "deserialization failed",
364 VibeEngineErrorCode::SerdeSerializeError => "serialization failed",
365 VibeEngineErrorCode::InvalidArgumentLogInfo => "invalid log info argument",
366 VibeEngineErrorCode::MPSCSendError => "channel send failed",
367 VibeEngineErrorCode::GenerateQRError => "QR generation failed",
368 VibeEngineErrorCode::RuntimeError => "runtime error",
369 VibeEngineErrorCode::BeaverError => "internal beaver error",
370 VibeEngineErrorCode::SocketRecvTimeout => "socket receive timed out",
371 VibeEngineErrorCode::SocketClosed => "socket closed",
372 VibeEngineErrorCode::ProtocParseError => "protocol parse failed",
373 VibeEngineErrorCode::SocketNotOpened => "socket is not opened",
374 VibeEngineErrorCode::TaskInterruptionError => "task interrupted",
375 VibeEngineErrorCode::ConnectionClosed => "connection closed",
376 VibeEngineErrorCode::ConnectionExists => "connection already exists",
377 VibeEngineErrorCode::ConnectionClosing => "connection is closing",
378 VibeEngineErrorCode::InternalError => "internal error",
379 VibeEngineErrorCode::NotLoggedInError => "not logged in",
380 VibeEngineErrorCode::PageTokenError => "page token error",
381 VibeEngineErrorCode::ClipboardInitializeError => "clipboard initialization failed",
382 VibeEngineErrorCode::NotSupportedYet => "not supported yet",
383 }
384}
385
386impl serde::Serialize for VibeEngineError {
387 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
388 where
389 S: serde::Serializer,
390 {
391 use serde::ser::SerializeStruct;
392 let mut s = serializer.serialize_struct("VibeEngineError", 5)?;
393 s.serialize_field("code", &self.code())?;
394 s.serialize_field("kind", &self.kind)?;
395 s.serialize_field("message", &self.message)?;
396 s.serialize_field("source", &self.source)?;
397 s.serialize_field("context", &self.context)?;
398 s.end()
399 }
400}
401
402#[cfg(test)]
403mod tests {
404 use super::*;
405
406 fn assert_std_error<T: std::error::Error>() {}
407
408 #[test]
409 fn error_model_exposes_kind_message_source_and_context() {
410 assert_std_error::<VibeEngineError>();
411
412 let error = VibeEngineError::from_error_code_msg(
413 VibeEngineErrorCode::TimeoutError,
414 "destroy timed out".to_string(),
415 )
416 .with_source("shutdown queue did not finish")
417 .with_context("engine.destroy_with_timeout");
418
419 assert_eq!(error.code(), VibeEngineErrorCode::TimeoutError.code());
420 assert_eq!(error.kind(), VibeErrorKind::Network);
421 assert_eq!(error.message(), "destroy timed out");
422 assert_eq!(
423 error.source_message(),
424 Some("shutdown queue did not finish")
425 );
426 assert_eq!(
427 error.context(),
428 &["engine.destroy_with_timeout".to_string()]
429 );
430 assert!(error.to_string().contains("destroy timed out"));
431 }
432}