ooxml_core/error.rs
1//! 错误类型与 `Result` 别名。
2//!
3//! 本模块是整个 `ooxml-core` crate 的错误处理中枢。它做三件事:
4//!
5//! 1. **统一错误类型** [`enum@Error`]:使用 `thiserror` 派生,将底层 `std::io::Error` /
6//! `zip::result::ZipError` / 自定义字符串错误统一成一个枚举,方便上层 `?` 传播。
7//! 2. **统一 Result 别名** [`Result<T>`]:所有公共 API(除特别声明)均返回该别名,
8//! 调用方不必书写冗长的 `Result<T, ooxml_core::Error>`。
9//! 3. **便捷构造器**:在 `Error` 上提供 `opc(...)` / `oxml(...)` / `not_implemented(...)`
10//! 等关联函数,使错误抛出更可读,并隐含语义归类。
11//!
12//! # 设计原则
13//!
14//! - **零 `panic!`**:库路径上禁止 `unwrap` / `expect` / `panic!`。所有失败一律
15//! 转化为 [`enum@Error`] 的一个变体,由调用方决定如何处理。
16//! - **错误消息规范**:消息小写开头、句末无标点(与 Rust 标准库惯例一致);
17//! 需要上下文时使用 `format!` 拼接具体元素名 / 路径,例如
18//! `"relationships parse: missing Id"`。
19//! - **可扩展**:新增错误类别时优先扩展 `enum Error` 变体,而非全部塞进
20//! [`Error::Other`]。后者仅用于临时过渡。
21//!
22//! # 与 python-pptx 的对应
23//!
24//! `python-pptx` 抛出 `python_pptx.PptxException` 及若干子异常(`PackageNotFoundError` /
25//! `XPathOverflowError` 等)。本库以单一枚举 + 字符串消息统一表达,对调用方而言
26//! 仅需 match 顶层 `Error` 即可。
27//!
28//! # 示例
29//!
30//! ```no_run
31//! use ooxml_core::{Error, Result};
32//!
33//! fn read_slide() -> Result<()> {
34//! let p = std::fs::File::open("missing.pptx")?; // io::Error 自动转 Error::Io
35//! Ok(())
36//! }
37//!
38//! fn parse_attr() -> Result<()> {
39//! Err(Error::oxml("missing required <p:ph> element"))
40//! }
41//! ```
42
43use std::io;
44use thiserror::Error;
45
46/// 库统一 `Result` 别名。
47///
48/// 简化签名书写,所有公共 API(除特别声明)均使用该别名,等价于
49/// `std::result::Result<T, ooxml_core::Error>`。
50///
51/// # 示例
52///
53/// ```no_run
54/// use ooxml_core::Result;
55///
56/// fn read_something() -> Result<String> { Ok(String::new()) }
57/// ```
58pub type Result<T> = std::result::Result<T, Error>;
59
60/// 库错误。所有外部接口(除特别声明外)均返回 [`Result<T>`]。
61///
62/// 变体按"来源"分类,调用方可以基于 `match` 决定重试 / 跳过 / 报告策略。
63/// 错误消息遵循 **小写开头、句末无标点** 的 Rust 标准库风格。
64#[derive(Debug, Error)]
65pub enum Error {
66 /// I/O 错误:文件不存在、读取失败、写入失败、权限拒绝等。
67 ///
68 /// 由 `#[from] io::Error` 自动派生,调用方可用 `?` 直接把任意
69 /// `std::io::Error` 提升为本变体。
70 #[error("I/O error: {0}")]
71 Io(#[from] io::Error),
72
73 /// zip 压缩包错误:CRC 校验失败、条目不存在、解压错误等。
74 ///
75 /// 由 `#[from] zip::result::ZipError` 派生,封装 `zip` crate 的全部错误。
76 #[error("zip error: {0}")]
77 Zip(#[from] zip::result::ZipError),
78
79 /// XML 解析或序列化错误。
80 ///
81 /// 字符串内容应包含 **出错元素名 + 上下文路径**,例如
82 /// `"slide layout1.xml parse: unexpected end of <p:sld> at line 42"`。
83 #[error("xml error: {0}")]
84 Xml(String),
85
86 /// OPC 包结构错误。
87 ///
88 /// 典型场景:
89 /// - 缺少必要的 part(如 `word/document.xml` 缺失);
90 /// - 关系链断裂(`r:id` 指向不存在的 target);
91 /// - `[Content_Types].xml` 中缺失 Override / Default。
92 #[error("OPC error: {0}")]
93 Opc(String),
94
95 /// OOXML 模型错误。
96 ///
97 /// 典型场景:
98 /// - 缺失必要字段(如 `slideLayout` 必须有 `cSld`);
99 /// - 命名空间不匹配;
100 /// - 序列化时违反 OOXML 元素顺序约束(CT_* schema 严格顺序)。
101 #[error("OOXML error: {0}")]
102 Oxml(String),
103
104 /// 元素未找到:按 Id / Name / Type 查找时未命中。
105 #[error("not found: {0}")]
106 NotFound(String),
107
108 /// 索引越界:访问 `Slides` / `Shapes` 等集合时 idx >= len。
109 #[error("index out of range: {0}")]
110 IndexOutOfRange(usize),
111
112 /// 不支持的功能(路线图中)。
113 ///
114 /// 携带 `&'static str` 描述功能名,便于编译期收集未实现项清单。
115 #[error("not implemented: {0}")]
116 NotImplemented(&'static str),
117
118 /// 加密/解密错误。
119 ///
120 /// 典型场景:
121 /// - 密码不匹配;
122 /// - 加密算法不支持;
123 /// - AES 密钥/IV 长度不正确;
124 /// - 加密文件格式损坏。
125 #[error("encryption error: {0}")]
126 Encryption(String),
127
128 /// 其它错误。
129 ///
130 /// **仅** 用于临时过渡;正式错误请扩展 `enum Error` 的具体变体。
131 #[error("{0}")]
132 Other(String),
133}
134
135impl Error {
136 /// 便捷构造:OPC 错误。
137 ///
138 /// 接受任何 `Into<String>` 的消息,避免调用方手动写 `Error::Opc(s.into())`。
139 pub fn opc<S: Into<String>>(msg: S) -> Self {
140 Error::Opc(msg.into())
141 }
142
143 /// 便捷构造:OOXML 错误。
144 pub fn oxml<S: Into<String>>(msg: S) -> Self {
145 Error::Oxml(msg.into())
146 }
147
148 /// 便捷构造:未实现。
149 ///
150 /// 配合 `unimplemented!` 风格的早期失败语义,用于在路线图功能
151 /// 调用点快速失败。
152 pub fn not_implemented(feature: &'static str) -> Self {
153 Error::NotImplemented(feature)
154 }
155
156 /// 便捷构造:加密/解密错误。
157 pub fn encryption<S: Into<String>>(msg: S) -> Self {
158 Error::Encryption(msg.into())
159 }
160}