Skip to main content

easydoc_core/
error.rs

1//! easydoc-rust 统一错误类型。
2//!
3//! 对标 easyexcel-rust 的单枚举错误模式。
4//!
5//! 对应 Java: 各类 checked/unchecked exception 合并为单一 Rust 错误类型。
6
7use thiserror::Error;
8
9/// 库统一错误枚举。
10///
11/// Java `EasyExcel` 将错误分散在七个 `RuntimeException` 子类中;
12/// 本枚举将它们合并为一个惯用的 Rust 类型。
13///
14/// 对应 Java: `com.alibaba.excel.exception.ExcelAnalysisException`、
15/// `ExcelGenerateException`、`ExcelDataConvertException` 等
16#[derive(Debug, Error)]
17#[non_exhaustive]
18pub enum DocError {
19    /// I/O 错误,包装 `std::io::Error`。
20    ///
21    /// 对应 Java: `IOException`
22    #[error("I/O error: {0}")]
23    Io(#[from] std::io::Error),
24
25    /// ZIP 包错误(来自 docx-rs 打包)。
26    ///
27    /// 对应 Java: `ExcelAnalysisException` 中的 ZIP 相关错误
28    #[error("ZIP error: {0}")]
29    Zip(String),
30
31    /// 无效或不支持的文档格式。
32    ///
33    /// 对应 Java: `ExcelAnalysisException("The supplied file was not supported")`
34    #[error("Format error: {0}")]
35    Format(String),
36
37    /// 模板占位符无法解析或处理。
38    ///
39    /// 对应 Java: `ExcelAnalysisException` 中的模板相关错误(easyexcel-template)
40    #[error("Template error at placeholder '{placeholder}': {message}")]
41    Template {
42        /// 导致错误的占位符标记。
43        placeholder: String,
44        /// 人类可读的描述。
45        message: String,
46    },
47
48    /// 单元格或字段值无法与目标类型互转。
49    ///
50    /// 对应 Java: `com.alibaba.excel.exception.ExcelDataConvertException`
51    #[error("Conversion error: field '{field}', value '{value}': {message}")]
52    Conversion {
53        /// 字段或列名。
54        field: String,
55        /// 转换失败的值。
56        value: String,
57        /// 人类可读的描述。
58        message: String,
59    },
60
61    /// 请求的操作不被当前格式或配置支持。
62    ///
63    /// 对应 Java: `UnsupportedOperationException`
64    #[error("Unsupported operation: {0}")]
65    Unsupported(String),
66
67    /// 通用文档级错误。
68    ///
69    /// 对应 Java: `ExcelAnalysisException` / `ExcelGenerateException`
70    #[error("Document error: {0}")]
71    Document(String),
72}
73
74/// easydoc-rust 标准 `Result` 类型别名。
75pub type Result<T> = std::result::Result<T, DocError>;
76
77impl From<zip::result::ZipError> for DocError {
78    fn from(e: zip::result::ZipError) -> Self {
79        Self::Zip(e.to_string())
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn error_display_io() {
89        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "missing");
90        let err = DocError::Io(io_err);
91        assert!(format!("{err}").contains("I/O error"));
92    }
93
94    #[test]
95    fn error_display_zip() {
96        let err = DocError::Zip("corrupt".into());
97        assert!(format!("{err}").contains("ZIP error"));
98    }
99
100    #[test]
101    fn error_display_format() {
102        let err = DocError::Format("bad xml".into());
103        assert!(format!("{err}").contains("Format error"));
104    }
105
106    #[test]
107    fn error_display_template() {
108        let err = DocError::Template {
109            placeholder: "name".into(),
110            message: "not found".into(),
111        };
112        let s = format!("{err}");
113        assert!(s.contains("name"));
114        assert!(s.contains("not found"));
115    }
116
117    #[test]
118    fn error_display_conversion() {
119        let err = DocError::Conversion {
120            field: "age".into(),
121            value: "abc".into(),
122            message: "not a number".into(),
123        };
124        let s = format!("{err}");
125        assert!(s.contains("age"));
126        assert!(s.contains("abc"));
127    }
128
129    #[test]
130    fn error_display_unsupported() {
131        let err = DocError::Unsupported("macro".into());
132        assert!(format!("{err}").contains("Unsupported operation"));
133    }
134
135    #[test]
136    fn error_display_document() {
137        let err = DocError::Document("corrupted".into());
138        assert!(format!("{err}").contains("Document error"));
139    }
140
141    #[test]
142    fn error_from_zip_error() {
143        let zip_err = zip::result::ZipError::FileNotFound;
144        let err: DocError = zip_err.into();
145        assert!(matches!(err, DocError::Zip(_)));
146    }
147
148    #[test]
149    fn error_from_io_error() {
150        let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "denied");
151        let err: DocError = io_err.into();
152        assert!(matches!(err, DocError::Io(_)));
153    }
154
155    #[test]
156    fn error_debug() {
157        let err = DocError::Document("test".into());
158        let dbg = format!("{err:?}");
159        assert!(dbg.contains("Document"));
160    }
161}