Skip to main content

mocra_core/errors/
error.rs

1#![allow(unused)]
2use serde::{Deserialize, Serialize};
3use std::error::Error as StdError;
4use std::fmt;
5use std::num::ParseIntError;
6use std::str::ParseBoolError;
7use thiserror::Error;
8// ProxyError has been extracted into its own crate, mocra-proxy; the main crate folds it into
9// its own Error at the boundary via the `From` impl below.
10use mocra_proxy::ProxyError;
11/// Generic error-detail type.
12pub type BoxError = Box<dyn StdError + Send + Sync + 'static>;
13
14pub type Result<T> = std::result::Result<T, Error>;
15
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17pub enum ErrorKind {
18    Request,
19    Response,
20    Command,
21    Service,
22    Proxy,
23    Download,
24    Queue,
25    Orm,
26    Task,
27    Module,
28    RateLimit,
29    ProcessorChain,
30    Parser,
31    DataMiddleware,
32    DataStore,
33    DynamicLibrary,
34    CacheService,
35}
36
37impl fmt::Display for ErrorKind {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        match self {
40            ErrorKind::Request => write!(f, "request"),
41            ErrorKind::Response => write!(f, "response"),
42            ErrorKind::Command => write!(f, "command"),
43            ErrorKind::Service => write!(f, "service"),
44            ErrorKind::Proxy => write!(f, "proxy"),
45            ErrorKind::Download => write!(f, "download"),
46            ErrorKind::Queue => write!(f, "queue"),
47            ErrorKind::Orm => write!(f, "orm"),
48            ErrorKind::Module => write!(f, "task"),
49            ErrorKind::RateLimit => write!(f, "rate limit"),
50            ErrorKind::ProcessorChain => write!(f, "processor chain"),
51            ErrorKind::Parser => write!(f, "parser"),
52            ErrorKind::DataMiddleware => write!(f, "data middleware"),
53            ErrorKind::DataStore => write!(f, "data store"),
54            ErrorKind::Task => write!(f, "task"),
55            ErrorKind::DynamicLibrary => write!(f, "dynamic library"),
56            ErrorKind::CacheService => write!(f, "cache service"),
57        }
58    }
59}
60
61pub struct ErrorInner {
62    pub kind: ErrorKind,
63    pub source: Option<BoxError>,
64    pub message: Option<String>,
65}
66
67pub struct Error {
68    pub inner: Box<ErrorInner>,
69}
70
71impl Error {
72    pub fn new<E>(kind: ErrorKind, source: Option<E>) -> Error
73    where
74        E: Into<BoxError>,
75    {
76        Error {
77            inner: Box::new(ErrorInner {
78                kind,
79                source: source.map(Into::into),
80                message: None,
81            }),
82        }
83    }
84
85    pub(crate) fn with_message<E>(kind: ErrorKind, message: String, source: Option<E>) -> Error
86    where
87        E: Into<BoxError>,
88    {
89        Error {
90            inner: Box::new(ErrorInner {
91                kind,
92                source: source.map(Into::into),
93                message: Some(message),
94            }),
95        }
96    }
97
98    pub fn kind(&self) -> &ErrorKind {
99        &self.inner.kind
100    }
101
102    pub fn is_request(&self) -> bool {
103        matches!(self.inner.kind, ErrorKind::Request)
104    }
105
106    pub fn is_response(&self) -> bool {
107        matches!(self.inner.kind, ErrorKind::Response)
108    }
109
110    pub fn is_command(&self) -> bool {
111        matches!(self.inner.kind, ErrorKind::Command)
112    }
113
114    pub fn is_service(&self) -> bool {
115        matches!(self.inner.kind, ErrorKind::Service)
116    }
117
118    pub fn is_proxy(&self) -> bool {
119        matches!(self.inner.kind, ErrorKind::Proxy)
120    }
121
122    pub fn is_download(&self) -> bool {
123        matches!(self.inner.kind, ErrorKind::Download)
124    }
125    pub fn is_queue(&self) -> bool {
126        matches!(self.inner.kind, ErrorKind::Queue)
127    }
128    pub fn is_timeout(&self) -> bool {
129        if let Some(source) = &self.inner.source {
130            // Check whether this is a timeout error.
131            source.to_string().to_lowercase().contains("timeout")
132        } else {
133            false
134        }
135    }
136
137    pub fn is_connect(&self) -> bool {
138        if let Some(source) = &self.inner.source {
139            let msg = source.to_string().to_lowercase();
140            msg.contains("connect") || msg.contains("connection")
141        } else {
142            false
143        }
144    }
145}
146
147impl fmt::Debug for Error {
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        let mut f = f.debug_struct("mocra::Error");
150        f.field("kind", &self.inner.kind);
151        if let Some(ref message) = self.inner.message {
152            f.field("message", message);
153        }
154        if let Some(ref source) = self.inner.source {
155            f.field("source", source);
156        }
157        f.finish()
158    }
159}
160
161impl fmt::Display for Error {
162    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163        if let Some(ref message) = self.inner.message {
164            write!(f, "{} error: {}", self.inner.kind, message)?;
165        } else {
166            write!(f, "{} error", self.inner.kind)?;
167        }
168
169        if let Some(ref source) = self.inner.source {
170            write!(f, ": {source}")?;
171        }
172
173        Ok(())
174    }
175}
176
177impl StdError for Error {
178    fn source(&self) -> Option<&(dyn StdError + 'static)> {
179        self.inner
180            .source
181            .as_ref()
182            .map(|e| &**e as &(dyn StdError + 'static))
183    }
184}
185
186/// Convenience macro for constructing the various kinds of errors.
187macro_rules! error {
188    ($kind:ident) => {
189        Error::new(ErrorKind::$kind, None::<BoxError>)
190    };
191    ($kind:ident, $src:expr) => {
192        Error::new(ErrorKind::$kind, Some($src))
193    };
194    ($kind:ident, $msg:expr, $src:expr) => {
195        Error::with_message(ErrorKind::$kind, $msg.to_string(), Some($src))
196    };
197}
198
199pub(crate) use error;
200
201// Conversions from specific error types into the generic error.
202impl From<RequestError> for Error {
203    fn from(err: RequestError) -> Self {
204        Error::new(ErrorKind::Request, Some(err))
205    }
206}
207
208impl From<ResponseError> for Error {
209    fn from(err: ResponseError) -> Self {
210        Error::new(ErrorKind::Response, Some(err))
211    }
212}
213
214impl From<CommandError> for Error {
215    fn from(err: CommandError) -> Self {
216        Error::new(ErrorKind::Command, Some(err))
217    }
218}
219
220impl From<ServiceError> for Error {
221    fn from(err: ServiceError) -> Self {
222        Error::new(ErrorKind::Service, Some(err))
223    }
224}
225
226impl From<ProxyError> for Error {
227    fn from(err: ProxyError) -> Self {
228        Error::new(ErrorKind::Proxy, Some(err))
229    }
230}
231
232impl From<CookieError> for Error {
233    fn from(err: CookieError) -> Self {
234        Error::new(ErrorKind::Request, Some(err))
235    }
236}
237
238impl From<DownloadError> for Error {
239    fn from(err: DownloadError) -> Self {
240        Error::new(ErrorKind::Download, Some(err))
241    }
242}
243impl From<QueueError> for Error {
244    fn from(err: QueueError) -> Self {
245        Error::new(ErrorKind::Queue, Some(err))
246    }
247}
248impl From<OrmError> for Error {
249    fn from(err: OrmError) -> Self {
250        Error::new(ErrorKind::Orm, Some(err))
251    }
252}
253
254impl From<ModuleError> for Error {
255    fn from(err: ModuleError) -> Self {
256        Error::new(ErrorKind::Module, Some(err))
257    }
258}
259
260impl From<HeaderError> for Error {
261    fn from(err: HeaderError) -> Self {
262        Error::new(ErrorKind::Request, Some(err))
263    }
264}
265impl From<RateLimitError> for Error {
266    fn from(err: RateLimitError) -> Self {
267        Error::new(ErrorKind::RateLimit, Some(err))
268    }
269}
270impl From<SyncError> for Error {
271    fn from(err: SyncError) -> Self {
272        Error::new(ErrorKind::CacheService, Some(err))
273    }
274}
275impl From<ProcessorChainError> for Error {
276    fn from(err: ProcessorChainError) -> Self {
277        Error::new(ErrorKind::ProcessorChain, Some(err))
278    }
279}
280impl From<ParserError> for Error {
281    fn from(value: ParserError) -> Self {
282        Error::new(ErrorKind::Parser, Some(value))
283    }
284}
285impl From<DataMiddlewareError> for Error {
286    fn from(value: DataMiddlewareError) -> Self {
287        Error::new(ErrorKind::DataMiddleware, Some(value))
288    }
289}
290
291impl From<DataStoreError> for Error {
292    fn from(value: DataStoreError) -> Self {
293        Error::new(ErrorKind::DataStore, Some(value))
294    }
295}
296impl From<TaskError> for Error {
297    fn from(value: TaskError) -> Self {
298        Error::new(ErrorKind::Task, Some(value))
299    }
300}
301
302impl From<DynLibError> for Error {
303    fn from(value: DynLibError) -> Self {
304        Error::new(ErrorKind::DynamicLibrary, Some(value))
305    }
306}
307
308// Concrete error type definitions.
309#[derive(Debug, Error)]
310pub enum RequestError {
311    #[error("invalid method")]
312    InvalidMethod(#[source] BoxError),
313    #[error("build failed")]
314    BuildFailed(#[source] BoxError),
315    #[error("bad request")]
316    BadRequest,
317    #[error("unauthorized")]
318    Unauthorized,
319    #[error("forbidden")]
320    Forbidden,
321    #[error("not found")]
322    NotFound,
323    #[error("timeout")]
324    Timeout,
325    #[error("invalid url: {0}")]
326    InvalidUrl(String),
327    #[error("invalid header: {0}")]
328    InvalidHeader(String),
329    #[error("invalid body: {0}")]
330    InvalidBody(String),
331    #[error("{0}")]
332    InvalidParams(#[source] BoxError),
333    #[error("{0}")]
334    NotLogin(#[source] BoxError),
335    #[error("{0}")]
336    NotImplemented(#[source] BoxError),
337    #[error("{0}")]
338    InvalidMetaForRemote(#[source] BoxError),
339}
340
341#[derive(Debug, Error)]
342pub enum ResponseError {
343    #[error("invalid format")]
344    InvalidFormat,
345    #[error("{0}")]
346    ParseError(BoxError),
347    #[error("empty response")]
348    EmptyResponse,
349    #[error("invalid status: {0}")]
350    InvalidStatus(u16),
351    #[error("body too large: {0} bytes")]
352    BodyTooLarge(usize),
353    #[error("decode error: {0}")]
354    DecodeError(String),
355}
356
357#[derive(Debug, Error)]
358pub enum CommandError {
359    #[error("invalid command")]
360    InvalidCommand,
361    #[error("execution failed")]
362    ExecutionFailed,
363    #[error("permission denied")]
364    PermissionDenied,
365    #[error("command not found: {0}")]
366    CommandNotFound(String),
367    #[error("invalid argument: {0}")]
368    InvalidArgument(String),
369}
370
371#[derive(Debug, Error)]
372pub enum ServiceError {
373    #[error("service unavailable")]
374    ServiceUnavailable,
375    #[error("connection failed")]
376    ConnectionFailed,
377    #[error("internal error")]
378    InternalError,
379    #[error("service timeout")]
380    ServiceTimeout,
381    #[error("rate limit exceeded")]
382    RateLimitExceeded,
383    #[error("authentication failed")]
384    AuthenticationFailed,
385}
386
387#[derive(Debug, Error)]
388pub enum CookieError {
389    #[error("{0}")]
390    ParseError(#[source] BoxError),
391    #[error("cookie not found: {0}")]
392    NotFound(#[source] BoxError),
393    #[error("cookie already exists: {0}")]
394    AlreadyExists(#[source] BoxError),
395    #[error("cookie load failed: {0}")]
396    LoadFailed(#[source] BoxError),
397    #[error("cookie save failed: {0}")]
398    SaveFailed(#[source] BoxError),
399    #[error("cookie domain mismatch: {0}")]
400    DomainMismatch(String),
401    #[error("cookie expired")]
402    Expired,
403    #[error("{0}")]
404    LoadError(#[source] BoxError),
405}
406#[derive(Debug, Error)]
407pub enum HeaderError {
408    #[error("header parse error: {0}")]
409    ParseError(#[source] BoxError),
410    #[error("header not found: {0}")]
411    NotFound(#[source] BoxError),
412    #[error("header already exists: {0}")]
413    AlreadyExists(#[source] BoxError),
414    #[error("header load failed: {0}")]
415    LoadFailed(#[source] BoxError),
416    #[error("header save failed: {0}")]
417    SaveFailed(#[source] BoxError),
418    #[error("{0}")]
419    LoadError(#[source] BoxError),
420}
421#[derive(Debug, Error)]
422pub enum DownloadError {
423    #[error("download failed: {0}")]
424    DownloadFailed(#[source] BoxError),
425    #[error("params error: {0}")]
426    InvalidParams(#[source] BoxError),
427    #[error("invalid url: {0}")]
428    InvalidUrl(#[source] BoxError),
429    #[error("invalid proxy: {0}")]
430    InvalidProxy(#[source] BoxError),
431    #[error("network error: {0}")]
432    NetworkError(#[source] BoxError),
433    #[error("timeout error: {0}")]
434    TimeoutError(#[source] BoxError),
435    #[error("client error: {0}")]
436    ClientError(#[source] BoxError),
437    #[error("invalid response with error: {0}")]
438    InvalidResponse(#[source] BoxError),
439    #[error("invalid method: {0}")]
440    InvalidMethod(#[source] BoxError),
441    #[error("file write error: {0}")]
442    FileWriteError(#[source] BoxError),
443    #[error("insufficient storage space")]
444    InsufficientSpace,
445    #[error("download interrupted")]
446    Interrupted,
447    #[error("max retry exceeded")]
448    MaxRetryExceeded,
449}
450
451#[derive(Debug, Error)]
452pub enum RateLimitError {
453    #[error("{0}")]
454    Backend(#[source] BoxError),
455    #[error("wait time: {0} seconds")]
456    WaitTime(u64),
457    #[error("wait time too long: {0} ms")]
458    WaitTimeTooLong(u64),
459}
460#[derive(Debug, Error)]
461pub enum SyncError {
462    #[error("{0}")]
463    ConnectFailed(#[source] BoxError),
464    #[error("{0}")]
465    SyncLoadFailed(#[source] BoxError),
466    #[error("{0}")]
467    SyncSaveFailed(#[source] BoxError),
468    #[error("{0}")]
469    SerdeFailed(#[source] BoxError),
470    #[error("{0}")]
471    DeserializeFailed(#[source] BoxError),
472    #[error("{0}")]
473    Timeout(#[source] BoxError),
474    #[error("{0}")]
475    ExecuteFailed(#[source] BoxError),
476}
477
478#[derive(Debug, Error)]
479pub enum QueueError {
480    #[error("data not serialization")]
481    SerializationFailed(BoxError),
482    #[error("data not deserialization")]
483    DeserializationFailed(BoxError),
484    #[error("connection failed")]
485    ConnectionFailed,
486    #[error("channel create failed: {0}")]
487    ChannelCreateFailed(BoxError),
488    #[error("channel not found: {0}")]
489    ChannelNotFound(BoxError),
490    #[error("push data to queue failed")]
491    PushFailed(BoxError),
492    #[error("receive from queue failed")]
493    PopFailed(BoxError),
494    #[error("queue operation failed: {0}")]
495    OperationFailed(#[source] BoxError),
496}
497
498#[derive(Debug, Error)]
499pub enum OrmError {
500    #[error("database connection error: {0}")]
501    ConnectionError(#[source] BoxError),
502    #[error("query execution error: {0}")]
503    QueryExecutionError(#[source] BoxError),
504    #[error("transaction error: {0}")]
505    TransactionError(#[source] BoxError),
506    #[error("data not found")]
507    NotFound,
508    #[error("data already exists")]
509    AlreadyExists,
510    #[error("invalid data: {0}")]
511    InvalidData(String),
512}
513#[derive(Debug, Error)]
514pub enum ModuleError {
515    #[error("json type cannot use error: {0}")]
516    Json(#[source] BoxError),
517    #[error("form type cannot use error: {0}")]
518    Form(#[source] BoxError),
519    #[error("params type cannot use error: {0}")]
520    Params(#[source] BoxError),
521    #[error("body must type of String: {0}")]
522    Body(#[source] BoxError),
523    #[error("{0}")]
524    Model(#[source] BoxError),
525    #[error("{0}")]
526    ConfigSync(#[source] BoxError),
527    #[error("{0}")]
528    ModuleNotFound(#[source] BoxError),
529    #[error("{0}")]
530    TaskMaxError(#[source] BoxError),
531    #[error("{0}")]
532    ModuleMaxError(#[source] BoxError),
533}
534#[derive(Debug, Error)]
535pub enum ProcessorChainError {
536    #[error("{0}")]
537    FatalFailure(#[source] BoxError),
538    #[error("{0}")]
539    DowncastFailure(#[source] BoxError),
540    #[error("{0}")]
541    MaxRetriesExceeded(#[source] BoxError),
542    #[error("{0}")]
543    EmptyChain(#[source] BoxError),
544    #[error("{0}")]
545    Unexpected(#[source] BoxError),
546    #[error("{0}")]
547    Timeout(#[source] BoxError),
548    #[error("{0}")]
549    Cancelled(#[source] BoxError),
550}
551
552#[derive(Debug, Error)]
553pub enum ParserError {
554    #[error("{0}")]
555    InvalidFormat(#[source] BoxError),
556    #[error("{0}")]
557    ParseError(#[source] BoxError),
558    #[error("{0}")]
559    EmptyResponse(#[source] BoxError),
560    #[error("{0}")]
561    InvalidStatus(#[source] BoxError),
562    #[error("{0}")]
563    InvalidMetaData(#[source] BoxError),
564    #[error("{0}")]
565    BodyTooLarge(#[source] BoxError),
566    #[error("{0}")]
567    DecodeError(#[source] BoxError),
568    #[error("{0}")]
569    PolarsError(#[source] BoxError),
570    #[error("{0}")]
571    JsonParseError(#[source] BoxError),
572    #[error("{0}")]
573    DataError(#[source] BoxError),
574}
575#[derive(Debug, Error)]
576pub enum DataMiddlewareError {
577    #[error("{0}")]
578    HandleDataError(#[source] BoxError),
579    #[error("{0}")]
580    InvalidData(#[source] BoxError),
581    #[error("{0}")]
582    EmptyData(#[source] BoxError),
583    #[error("{0}")]
584    MiddlewareNotFound(#[source] BoxError),
585}
586#[derive(Debug, Error)]
587pub enum DataStoreError {
588    #[error("{0}")]
589    SaveFailed(#[source] BoxError),
590    #[error("{0}")]
591    InvalidData(#[source] BoxError),
592    #[error("{0}")]
593    ConnectionFailed(#[source] BoxError),
594    #[error("{0}")]
595    TransactionFailed(#[source] BoxError),
596}
597
598#[derive(Debug, Error)]
599pub enum TaskError {
600    #[error("{0}")]
601    TaskNotFound(#[source] BoxError),
602    #[error("{0}")]
603    TaskAlreadyExists(#[source] BoxError),
604    #[error("{0}")]
605    InvalidTaskConfig(#[source] BoxError),
606    #[error("{0}")]
607    TaskExecutionFailed(#[source] BoxError),
608    #[error("{0}")]
609    TaskTimeout(#[source] BoxError),
610    #[error("{0}")]
611    TaskMaxError(#[source] BoxError),
612}
613
614#[derive(Debug, Error)]
615pub enum DynLibError {
616    #[error("{0}")]
617    LoadError(#[source] BoxError),
618    #[error("{0}")]
619    SymbolNotFound(#[source] BoxError),
620    #[error("{0}")]
621    InitializationFailed(#[source] BoxError),
622    #[error("{0}")]
623    ExecutionFailed(#[source] BoxError),
624    #[error("{0}")]
625    IncompatibleVersion(#[source] BoxError),
626    #[error("{0}")]
627    InvalidPath(#[source] BoxError),
628    #[error("{0}")]
629    DependencyError(#[source] BoxError),
630    #[error("{0}")]
631    Other(#[source] BoxError),
632}
633
634#[derive(Error, Debug)]
635pub enum CacheError {
636    #[error("Pool error: {0}")]
637    Pool(String),
638    #[error("Serialization error: {0}")]
639    Serde(#[from] serde_json::Error),
640    #[error("Service not initialized")]
641    NotInitialized,
642    #[error("NotFound")]
643    NotFound,
644    #[error("IO error: {0}")]
645    Io(#[from] std::io::Error),
646}
647
648// Convenience constructors for the common error types.
649impl Error {
650    pub fn request_timeout() -> Self {
651        Error::from(RequestError::Timeout)
652    }
653
654    pub fn request_not_found() -> Self {
655        Error::from(RequestError::NotFound)
656    }
657
658    pub fn proxy_not_found() -> Self {
659        Error::from(ProxyError::ProxyNotFound)
660    }
661
662    pub fn proxy_expired() -> Self {
663        Error::from(ProxyError::ProxyExpired)
664    }
665
666    pub fn download_failed<E: Into<BoxError>>(source: E) -> Self {
667        Error::from(DownloadError::DownloadFailed(source.into()))
668    }
669
670    pub fn cookie_parse_error<E: Into<BoxError>>(source: E) -> Self {
671        Error::from(CookieError::ParseError(source.into()))
672    }
673
674    pub fn service_unavailable() -> Self {
675        Error::from(ServiceError::ServiceUnavailable)
676    }
677}
678
679// Conversions for the common external error types.
680impl From<std::io::Error> for Error {
681    fn from(err: std::io::Error) -> Self {
682        match err.kind() {
683            std::io::ErrorKind::TimedOut => Error::request_timeout(),
684            std::io::ErrorKind::ConnectionRefused => Error::from(ServiceError::ConnectionFailed),
685            std::io::ErrorKind::PermissionDenied => Error::from(CommandError::PermissionDenied),
686            std::io::ErrorKind::NotFound => Error::request_not_found(),
687            _ => Error::new(ErrorKind::Service, Some(err)),
688        }
689    }
690}
691impl From<CacheError> for Error {
692    fn from(err: CacheError) -> Self {
693        Error::new(ErrorKind::CacheService, Some(err))
694    }
695}
696impl From<ParseIntError> for Error {
697    fn from(value: ParseIntError) -> Self {
698        Error::new(ErrorKind::Parser, Some(value))
699    }
700}
701impl From<ParseBoolError> for Error {
702    fn from(value: ParseBoolError) -> Self {
703        Error::new(ErrorKind::Parser, Some(value))
704    }
705}
706
707impl From<serde_json::Error> for Error {
708    fn from(err: serde_json::Error) -> Self {
709        Error::from(ResponseError::ParseError(err.to_string().into()))
710    }
711}
712
713#[cfg(feature = "polars")]
714impl From<polars::prelude::PolarsError> for Error {
715    fn from(err: polars::prelude::PolarsError) -> Self {
716        Error::from(ParserError::PolarsError(err.to_string().into()))
717    }
718}
719
720#[cfg(test)]
721mod tests {
722    use super::*;
723
724    #[test]
725    fn test_error_creation() {
726        let err = Error::request_timeout();
727        assert!(err.is_request());
728        assert!(err.is_timeout());
729    }
730
731    #[test]
732    fn test_error_display() {
733        let err = Error::request_not_found();
734        assert_eq!(err.to_string(), "request error: not found");
735    }
736
737    #[test]
738    fn test_error_source() {
739        let io_err = std::io::Error::new(std::io::ErrorKind::TimedOut, "connection timed out");
740        let err = Error::from(io_err);
741        assert!(err.source().is_some());
742    }
743
744    #[test]
745    fn test_error_kinds() {
746        let err = Error::proxy_not_found();
747        assert!(err.is_proxy());
748        assert!(!err.is_request());
749
750        let err = Error::service_unavailable();
751        assert!(err.is_service());
752        assert!(!err.is_proxy());
753    }
754}