Skip to main content

docparser_core/
error.rs

1//! 统一错误分类(与 Python `errors.py` / KB 错误语义对齐)。
2
3use std::fmt;
4
5/// 全局统一错误码。
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7pub enum ErrorCode {
8    Encrypted,   // 加密 / 权限受限
9    Corrupt,     // 文件损坏 / 后端链耗尽
10    Unsupported, // 格式不支持 / 伪装
11    Timeout,     // 单文件 / 单页超时
12    NativeCrash, // 底层 native 崩溃
13    Unknown,
14}
15
16impl ErrorCode {
17    pub fn as_str(&self) -> &'static str {
18        match self {
19            ErrorCode::Encrypted => "encrypted",
20            ErrorCode::Corrupt => "corrupt",
21            ErrorCode::Unsupported => "unsupported",
22            ErrorCode::Timeout => "timeout",
23            ErrorCode::NativeCrash => "native_crash",
24            ErrorCode::Unknown => "unknown",
25        }
26    }
27}
28
29impl fmt::Display for ErrorCode {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        f.write_str(self.as_str())
32    }
33}
34
35/// 解析错误:统一码 + 可读描述 + 已尝试后端序列。
36#[derive(Debug, Clone)]
37pub struct DocError {
38    pub code: ErrorCode,
39    pub message: String,
40    pub attempts: Vec<String>,
41}
42
43impl DocError {
44    pub fn new(code: ErrorCode, message: impl Into<String>) -> Self {
45        Self {
46            code,
47            message: message.into(),
48            attempts: Vec::new(),
49        }
50    }
51    pub fn encrypted(m: impl Into<String>) -> Self {
52        Self::new(ErrorCode::Encrypted, m)
53    }
54    pub fn corrupt(m: impl Into<String>) -> Self {
55        Self::new(ErrorCode::Corrupt, m)
56    }
57    pub fn unsupported(m: impl Into<String>) -> Self {
58        Self::new(ErrorCode::Unsupported, m)
59    }
60    pub fn timeout(m: impl Into<String>) -> Self {
61        Self::new(ErrorCode::Timeout, m)
62    }
63}
64
65impl fmt::Display for DocError {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        write!(f, "[{}] {}", self.code, self.message)
68    }
69}
70
71impl std::error::Error for DocError {}
72
73pub type Result<T> = std::result::Result<T, DocError>;
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn code_display_and_constructors() {
81        assert_eq!(ErrorCode::Encrypted.as_str(), "encrypted");
82        assert_eq!(DocError::encrypted("x").code, ErrorCode::Encrypted);
83        assert_eq!(DocError::corrupt("x").code, ErrorCode::Corrupt);
84        assert_eq!(DocError::unsupported("x").code, ErrorCode::Unsupported);
85        assert_eq!(DocError::timeout("x").code, ErrorCode::Timeout);
86        assert_eq!(format!("{}", DocError::corrupt("bad")), "[corrupt] bad");
87    }
88
89    #[test]
90    fn error_is_send_and_boxable() {
91        let e: Box<dyn std::error::Error + Send> = Box::new(DocError::corrupt("x"));
92        assert!(e.to_string().contains("corrupt"));
93    }
94}