1use quick_xml::Error as XmlError;
2use serde_json::Error as JsonError;
3use std::error::Error;
4use std::io;
5use thiserror::Error;
6use zip::result::ZipError;
7
8pub use category::ErrorCategory;
9pub use category::ErrorSeverity;
10pub use format::file::FileError;
11pub use format::parse::ParseError;
12pub use format::xlsx::XlsxError;
13pub use format::docx::DocxError;
14pub use format::pptx::PptxError;
15
16use crate::code::RecoverySuggestion;
17use crate::error::code::*; use crate::error::monitor::{ ErrorMonitor, ErrorStats };
19use crate::error::code::RecoveryAction;
20use crate::error::context::ErrorContext;
21use crate::error::monitor::error_monitor;
22
23pub mod mod_backup;
24pub mod category;
25pub mod format;
26pub mod code;
27pub mod context;
28pub mod monitor;
29
30#[derive(Error, Debug)]
32pub enum OfficeError {
33 #[error("IO错误: {0}")]
35 Io(#[from] io::Error),
36
37 #[error("ZIP文件错误: {0}")]
39 Zip(#[from] ZipError),
40
41 #[error("XML解析错误: {0}")]
43 Xml(#[from] XmlError),
44
45 #[error("JSON错误: {0}")]
47 Json(#[from] JsonError),
48
49 #[error("编码错误: {0}")]
51 Encoding(#[from] std::string::FromUtf8Error),
52
53 #[error("文件错误: {0}")]
55 File(#[from] FileError),
56
57 #[error("解析错误: {0}")]
59 Parse(#[from] ParseError),
60
61 #[error("Excel错误: {0}")]
63 Xlsx(#[from] XlsxError),
64
65 #[error("Word错误: {0}")]
67 Docx(#[from] DocxError),
68
69 #[error("PowerPoint错误: {0}")]
71 Pptx(#[from] PptxError),
72
73 #[error("格式错误: {0}")]
75 Format(String),
76
77 #[error("不支持的文件类型: {0}")]
79 UnsupportedFormat(String),
80
81 #[error("文档结构错误: {0}")]
83 Structure(String),
84
85 #[error("其他错误: {0}")]
87 Other(String),
88
89 #[error("{error}\n上下文: {context:?}")]
91 WithContext {
92 error: Box<OfficeError>,
93 context: ErrorContext,
94 },
95}
96
97impl OfficeError {
98 pub fn file_not_found_with_context(path: String, context: ErrorContext) -> Self {
100 Self::File(FileError::NotFound { path }).with_context(context)
101 }
102
103 pub fn parse_error_with_context(element: String, context: ErrorContext) -> Self {
105 Self::Parse(ParseError::MissingElement { element }).with_context(context)
106 }
107
108 pub fn xlsx_error_with_context(name: String, context: ErrorContext) -> Self {
110 Self::Xlsx(XlsxError::WorksheetNotFound { name }).with_context(context)
111 }
112
113 pub fn record(&self) -> &Self {
115 error_monitor().record_error(self);
116 self
117 }
118
119 pub fn new_and_record(error: OfficeError) -> Self {
121 error_monitor().record_error(&error);
122 error
123 }
124 pub fn severity(&self) -> ErrorSeverity {
126 match self {
127 Self::Io(_) => ErrorSeverity::Fatal,
128 Self::Zip(_) => ErrorSeverity::Fatal,
129 Self::Xml(_) => ErrorSeverity::Error,
130 Self::Json(_) => ErrorSeverity::Error,
131 Self::Encoding(_) => ErrorSeverity::Error,
132 Self::File(FileError::NotFound { .. }) => ErrorSeverity::Fatal,
133 Self::File(FileError::PermissionDenied { .. }) => ErrorSeverity::Fatal,
134 Self::File(FileError::Corrupted { .. }) => ErrorSeverity::Fatal,
135 Self::File(FileError::InvalidFormat { .. }) => ErrorSeverity::Error,
136 Self::Parse(_) => ErrorSeverity::Error,
137 Self::Xlsx(_) => ErrorSeverity::Error,
138 Self::Docx(_) => ErrorSeverity::Error,
139 Self::Pptx(_) => ErrorSeverity::Error,
140 Self::Format(_) => ErrorSeverity::Error,
141 Self::UnsupportedFormat(_) => ErrorSeverity::Fatal,
142 Self::Structure(_) => ErrorSeverity::Warning,
143 Self::Other(_) => ErrorSeverity::Error,
144 Self::WithContext { error, .. } => error.severity(),
145 }
146 }
147
148 pub fn category(&self) -> ErrorCategory {
150 match self {
151 Self::Io(_) | Self::File(_) => ErrorCategory::FileSystem,
152 Self::Zip(_) => ErrorCategory::FileSystem,
153 Self::Xml(_) | Self::Json(_) | Self::Encoding(_) => ErrorCategory::Parsing,
154 Self::Parse(_) => ErrorCategory::Parsing,
155 Self::Xlsx(_) | Self::Docx(_) | Self::Pptx(_) => ErrorCategory::Validation,
156 Self::Format(_) | Self::Structure(_) => ErrorCategory::Validation,
157 Self::UnsupportedFormat(_) => ErrorCategory::Parsing,
158 Self::Other(_) => ErrorCategory::Internal,
159 Self::WithContext { error, .. } => error.category(),
160 }
161 }
162
163 pub fn error_code(&self) -> ErrorCode {
165 match self {
166 Self::Io(_) => ErrorCode::System(SystemErrorCode::IoError),
168 Self::Zip(_) => ErrorCode::System(SystemErrorCode::ZipError),
169 Self::File(FileError::NotFound { .. }) => ErrorCode::System(SystemErrorCode::NotFound),
170 Self::File(FileError::PermissionDenied { .. }) =>
171 ErrorCode::System(SystemErrorCode::PermissionDenied),
172 Self::File(FileError::Corrupted { .. }) =>
173 ErrorCode::System(SystemErrorCode::Corrupted),
174 Self::File(FileError::InvalidFormat { .. }) =>
175 ErrorCode::Format(FormatErrorCode::InvalidFormat),
176
177 Self::Xml(_) => ErrorCode::Parse(ParseErrorCode::Xml),
179 Self::Json(_) => ErrorCode::Parse(ParseErrorCode::Json),
180 Self::Encoding(_) => ErrorCode::Parse(ParseErrorCode::Encoding),
181 Self::Parse(parse_err) =>
182 match parse_err {
183 ParseError::MissingElement { .. } =>
184 ErrorCode::Parse(ParseErrorCode::MissingElement),
185 ParseError::InvalidAttribute { .. } =>
186 ErrorCode::Parse(ParseErrorCode::InvalidAttribute),
187 ParseError::UnsupportedVersion { .. } =>
188 ErrorCode::Parse(ParseErrorCode::UnsupportedVersion),
189 ParseError::InvalidNamespace { .. } =>
190 ErrorCode::Parse(ParseErrorCode::InvalidNamespace),
191 }
192
193 Self::Xlsx(xlsx_err) =>
195 match xlsx_err {
196 XlsxError::WorksheetNotFound { .. } =>
197 ErrorCode::Document(DocumentErrorCode::XlsxWorksheetNotFound),
198 XlsxError::InvalidCellReference { .. } =>
199 ErrorCode::Document(DocumentErrorCode::XlsxInvalidCellReference),
200 XlsxError::InvalidFormula { .. } =>
201 ErrorCode::Document(DocumentErrorCode::XlsxInvalidFormula),
202 XlsxError::InvalidStyleId { .. } =>
203 ErrorCode::Document(DocumentErrorCode::XlsxInvalidStyle),
204 XlsxError::SharedStringIndexOutOfRange { .. } =>
205 ErrorCode::Format(FormatErrorCode::OutOfRange),
206 XlsxError::WorksheetCreationFailed { .. } =>
207 ErrorCode::Document(DocumentErrorCode::XlsxWorksheetNotFound),
208 }
209
210 Self::Docx(docx_err) =>
212 match docx_err {
213 DocxError::StyleNotFound { .. } =>
214 ErrorCode::Document(DocumentErrorCode::DocxStyleNotFound),
215 DocxError::InvalidTableStructure =>
216 ErrorCode::Document(DocumentErrorCode::DocxInvalidTableStructure),
217 DocxError::InvalidParagraphFormat { .. } =>
218 ErrorCode::Document(DocumentErrorCode::DocxInvalidParagraph),
219 DocxError::BookmarkNotFound { .. } =>
220 ErrorCode::Document(DocumentErrorCode::DocxBookmarkNotFound),
221 }
222
223 Self::Pptx(_) => ErrorCode::Document(DocumentErrorCode::PptxInvalidPresentation),
225
226 Self::Format(_) => ErrorCode::Format(FormatErrorCode::InvalidFormat),
228 Self::UnsupportedFormat(_) => ErrorCode::Format(FormatErrorCode::UnsupportedFormat),
229 Self::Structure(_) => ErrorCode::Common(CommonErrorCode::InvalidOperation),
230 Self::Other(_) => ErrorCode::Common(CommonErrorCode::UnknownError),
231
232 Self::WithContext { error, .. } => error.error_code(),
234 }
235 }
236
237 pub fn is_recoverable(&self) -> bool {
239 matches!(self.severity(), ErrorSeverity::Warning | ErrorSeverity::Error)
240 }
241
242 pub fn with_context(self, context: ErrorContext) -> Self {
244 let error_with_context = Self::WithContext {
245 error: Box::new(self),
246 context,
247 };
248 error_monitor().record_error(&error_with_context);
250 error_with_context
251 }
252
253 pub fn context(&self) -> Option<&ErrorContext> {
255 match self {
256 Self::WithContext { context, .. } => Some(context),
257 _ => None,
258 }
259 }
260
261 pub fn root_error(&self) -> &OfficeError {
263 match self {
264 Self::WithContext { error, .. } => error.root_error(),
265 _ => self,
266 }
267 }
268
269 pub fn recovery_suggestion(&self) -> Option<RecoverySuggestion> {
271 match self.root_error() {
272 Self::File(FileError::NotFound { path }) =>
273 Some(RecoverySuggestion {
274 message: format!("检查文件路径是否正确: {}", path),
275 action: RecoveryAction::UserInput("请提供正确的文件路径".to_string()),
276 }),
277 Self::File(FileError::PermissionDenied { path }) =>
278 Some(RecoverySuggestion {
279 message: format!("检查文件权限: {}", path),
280 action: RecoveryAction::UserInput("请确保有足够的文件访问权限".to_string()),
281 }),
282 Self::Xlsx(XlsxError::WorksheetNotFound { name }) =>
283 Some(RecoverySuggestion {
284 message: format!("工作表 '{}' 不存在,可以使用默认工作表", name),
285 action: RecoveryAction::UseDefault,
286 }),
287 Self::Xlsx(XlsxError::InvalidCellReference { reference }) =>
288 Some(RecoverySuggestion {
289 message: format!("单元格引用 '{}' 无效,跳过此单元格", reference),
290 action: RecoveryAction::SkipElement,
291 }),
292 Self::Docx(DocxError::StyleNotFound { style_id }) =>
293 Some(RecoverySuggestion {
294 message: format!("样式 '{}' 不存在,使用默认样式", style_id),
295 action: RecoveryAction::UseDefault,
296 }),
297 Self::Pptx(PptxError::SlideNotFound { slide_id }) =>
298 Some(RecoverySuggestion {
299 message: format!("幻灯片 '{}' 不存在,跳过此幻灯片", slide_id),
300 action: RecoveryAction::SkipElement,
301 }),
302 Self::Structure(_) =>
303 Some(RecoverySuggestion {
304 message: "文档结构异常,但可以继续处理".to_string(),
305 action: RecoveryAction::SkipElement,
306 }),
307 Self::Io(_) | Self::Zip(_) | Self::UnsupportedFormat(_) =>
308 Some(RecoverySuggestion {
309 message: "致命错误,无法恢复".to_string(),
310 action: RecoveryAction::None,
311 }),
312 _ =>
313 Some(RecoverySuggestion {
314 message: "可以重试操作".to_string(),
315 action: RecoveryAction::Retry(3),
316 }),
317 }
318 }
319
320 pub fn error_chain(&self) -> Vec<&dyn std::error::Error> {
322 let mut chain = vec![self as &dyn std::error::Error];
323 let mut source = self.source();
324 while let Some(err) = source {
325 chain.push(err);
326 source = err.source();
327 }
328 chain
329 }
330
331 pub fn root_cause(&self) -> &dyn std::error::Error {
333 self.error_chain().into_iter().last().unwrap()
334 }
335
336 pub fn try_recover(&self, strategy: RecoveryStrategy) -> Result<()> {
337 match strategy.operation {
340 RecoveryAction::Retry(times) => {
341 for _ in 0..times {
342 if self.is_recoverable() {
344 return Ok(());
345 }
346 }
347 Err(Self::Other("重试失败".to_string()))
348 }
349 RecoveryAction::Fallback(fallback) => {
350 println!("执行降级方案: {}", fallback);
352 todo!("Implement fallback logic");
353 Ok(())
354 }
355 RecoveryAction::Ignore => Ok(()),
356 _ => { Ok(()) }
357 }
358 }
359}
360
361pub type Result<T> = std::result::Result<T, OfficeError>;