Skip to main content

parse_book_source/
error.rs

1//! 分层错误(v2)。fetch/verify 各层错误在对应阶段实现时并入。
2
3use thiserror::Error;
4
5/// 书源 v2 顶层错误。
6#[derive(Debug, Error)]
7pub enum BookSourceError {
8    /// 配置(书源 JSON)解析失败。
9    #[error("book source config error: {0}")]
10    Config(#[from] ConfigError),
11    /// 规则求值失败。
12    #[error("rule eval error: {0}")]
13    Eval(#[from] EvalError),
14    /// 取页失败。
15    #[error("fetch error: {0}")]
16    Fetch(#[from] FetchError),
17    /// 操作所需的配置缺失(如未配置 search/explore)。
18    #[error("book source missing config: {0}")]
19    Missing(&'static str),
20}
21
22impl BookSourceError {
23    /// 是否为「被反爬挑战拦截」(如 Cloudflare 托管挑战)。
24    /// 用于诊断/降级:据此给出精确提示而非笼统失败,并决定是否升级浏览器取页。
25    pub fn is_challenge(&self) -> bool {
26        matches!(self, BookSourceError::Fetch(FetchError::Challenged(_)))
27    }
28}
29
30/// 取页层错误。
31#[derive(Debug, Error)]
32pub enum FetchError {
33    /// 网络/HTTP 错误。
34    #[error("http error: {0}")]
35    Http(#[from] reqwest::Error),
36    /// 非法请求头。
37    #[error("invalid header: {0}")]
38    Header(String),
39    /// 响应解码失败。
40    #[error("decode error: {0}")]
41    Decode(String),
42    /// 被反爬挑战拦截(如 Cloudflare 托管挑战):拿到的是挑战页而非真实内容。
43    #[error("blocked by anti-bot challenge: {0}")]
44    Challenged(String),
45    /// 浏览器解挑战失败(仅 `browser` feature)。
46    #[cfg(feature = "browser")]
47    #[error("browser solve error: {0}")]
48    Browser(String),
49}
50
51/// 配置层错误。
52#[derive(Debug, Error)]
53pub enum ConfigError {
54    /// 书源 JSON 反序列化失败。
55    #[error("invalid book source json: {0}")]
56    Json(#[from] serde_json::Error),
57    /// 读取本地书源文件失败。
58    #[error("read book source file: {0}")]
59    Io(#[from] std::io::Error),
60}
61
62/// 规则求值层错误。
63#[derive(Debug, Error)]
64pub enum EvalError {
65    /// CSS 选择器非法。
66    #[error("invalid css selector: {0}")]
67    Selector(String),
68    /// 正则非法。
69    #[error("invalid regex: {0}")]
70    Regex(String),
71    /// JSONPath 查询失败。
72    #[error("jsonpath error: {0}")]
73    JsonPath(String),
74    /// XPath 表达式非法或求值失败。
75    #[error("xpath error: {0}")]
76    Xpath(String),
77    /// 待解析内容不是合法 JSON。
78    #[error("invalid json content: {0}")]
79    Json(String),
80    /// 该 via 后端暂未启用(如 xpath)。
81    #[error("extraction backend not enabled: {0}")]
82    Unsupported(&'static str),
83    /// clean 编解码算子失败(非法 base64/hex/url 等)。
84    #[error("codec error: {0}")]
85    Codec(String),
86    /// clean 加解密算子失败(密钥/IV 长度错、padding 错、密文损坏等)。
87    #[error("crypto error: {0}")]
88    Crypto(String),
89    /// JS 脚本求值失败(语法错/运行错;仅 `js` feature)。
90    #[error("js error: {0}")]
91    Js(String),
92    /// clean 字体反爬还原算子失败(未知内置表 / 非法码点 / 缺映射表)。
93    #[error("font map error: {0}")]
94    Font(String),
95}
96
97/// v2 结果别名。
98pub type Result<T> = std::result::Result<T, BookSourceError>;