Skip to main content

rusty_cat/
error.rs

1use std::error::Error as StdError;
2use std::fmt::{Display, Formatter};
3use std::sync::Arc;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub enum InnerErrorCode {
7    Unknown = -1,
8    /// 成功
9    Success = 0,
10    ///
11    RuntimeCreationFailedError = 101,
12
13    ParameterEmpty = 102,
14
15    /// the same file is already queued or running
16    DuplicateTaskError = 103,
17    EnqueueError = 104,
18
19    IoError = 105,
20    HttpError = 106,
21    /// 客户端已经执行过 close,不可再提交或控制任务。
22    ClientClosed = 107,
23    /// 控制接口收到未知 task_id(例如任务已结束或 id 不存在)。
24    TaskNotFound = 108,
25    ResponseStatusError = 109,
26    MissingOrInvalidContentLengthFromHead = 110,
27    /// 控制命令发送到调度线程失败(队列关闭/线程退出等)。
28    CommandSendFailed = 111,
29    /// 控制命令已发送,但应答通道异常关闭。
30    CommandResponseFailed = 112,
31    /// JSON 等响应体解析失败。
32    ResponseParseError = 113,
33    /// HTTP Range 协议非法(状态、Content-Range、偏移等不一致)。
34    InvalidRange = 114,
35    /// 本地文件不存在(常见于上传源文件丢失)。
36    FileNotFound = 115,
37    /// 文件校验失败(例如签名/摘要不匹配)。
38    ChecksumMismatch = 116,
39    /// 任务当前状态不允许该操作(例如 resume 非 paused 任务)。
40    InvalidTaskState = 117,
41    /// 内部锁被 poison,无法安全读取/写入共享状态。
42    LockPoisoned = 118,
43    /// 构建内置 HTTP 客户端失败。
44    HttpClientBuildFailed = 119,
45}
46
47#[derive(Debug, Clone)]
48pub struct MeowError {
49    /// [InnerErrorCode]
50    code: i32,
51    msg: String,
52    source: Option<Arc<dyn StdError + Send + Sync>>,
53}
54
55impl MeowError {
56    pub fn new(code: i32, msg: String) -> Self {
57        crate::log::emit_lazy(|| {
58            crate::log::Log::debug("error", format!("MeowError::new code={} msg={}", code, msg))
59        });
60        MeowError {
61            code,
62            msg,
63            source: None,
64        }
65    }
66
67    pub fn code(&self) -> i32 {
68        self.code
69    }
70
71    pub fn msg(&self) -> String {
72        self.msg.clone()
73    }
74
75    pub fn from_code1(code: InnerErrorCode) -> Self {
76        crate::log::emit_lazy(|| {
77            crate::log::Log::debug("error", format!("MeowError::from_code1 code={:?}", code))
78        });
79        MeowError {
80            code: code as i32,
81            msg: String::new(),
82            source: None,
83        }
84    }
85
86    pub fn from_code(code: InnerErrorCode, msg: String) -> Self {
87        crate::log::emit_lazy(|| {
88            crate::log::Log::debug(
89                "error",
90                format!("MeowError::from_code code={:?} msg={}", code, msg),
91            )
92        });
93        MeowError {
94            code: code as i32,
95            msg,
96            source: None,
97        }
98    }
99
100    pub fn from_code_str(code: InnerErrorCode, msg: &str) -> Self {
101        crate::log::emit_lazy(|| {
102            crate::log::Log::debug(
103                "error",
104                format!("MeowError::from_code_str code={:?} msg={}", code, msg),
105            )
106        });
107        MeowError {
108            code: code as i32,
109            msg: msg.to_string(),
110            source: None,
111        }
112    }
113
114    pub fn from_source<E>(code: InnerErrorCode, msg: impl Into<String>, source: E) -> Self
115    where
116        E: StdError + Send + Sync + 'static,
117    {
118        let msg = msg.into();
119        let source_preview = source.to_string();
120        crate::log::emit_lazy(|| {
121            crate::log::Log::debug(
122                "error",
123                format!(
124                    "MeowError::from_source code={:?} msg={} source={}",
125                    code, msg, source_preview
126                ),
127            )
128        });
129        MeowError {
130            code: code as i32,
131            msg,
132            source: Some(Arc::new(source)),
133        }
134    }
135}
136
137impl PartialEq for MeowError {
138    fn eq(&self, other: &Self) -> bool {
139        self.code == other.code && self.msg == other.msg
140    }
141}
142
143impl Eq for MeowError {}
144
145impl Display for MeowError {
146    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
147        if self.msg.is_empty() {
148            write!(f, "MeowError(code={})", self.code)
149        } else {
150            write!(f, "MeowError(code={}, msg={})", self.code, self.msg)
151        }
152    }
153}
154
155impl StdError for MeowError {
156    fn source(&self) -> Option<&(dyn StdError + 'static)> {
157        self.source
158            .as_deref()
159            .map(|e| e as &(dyn StdError + 'static))
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use super::{InnerErrorCode, MeowError};
166
167    #[test]
168    fn meow_error_display_contains_code_and_message() {
169        let err = MeowError::from_code_str(InnerErrorCode::InvalidRange, "bad range");
170        let s = format!("{err}");
171        assert!(s.contains("code="));
172        assert!(s.contains("bad range"));
173    }
174
175    #[test]
176    fn meow_error_source_is_accessible() {
177        let io = std::io::Error::other("disk io");
178        let err = MeowError::from_source(InnerErrorCode::IoError, "io failed", io);
179        assert!(std::error::Error::source(&err).is_some());
180    }
181}