Skip to main content

office_rs/error/
mod_backup.rs

1use quick_xml::Error as XmlError;
2use serde_json::Error as JsonError;
3use std::collections::HashMap;
4use std::error::Error;
5use std::io;
6use std::sync::atomic::{ AtomicU64, Ordering };
7use std::sync::{ Mutex, OnceLock };
8use std::time::{ SystemTime, UNIX_EPOCH };
9use thiserror::Error;
10use zip::result::ZipError;
11
12/// 错误严重程度
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum ErrorSeverity {
15    /// 警告,可以继续处理
16    Warning,
17    /// 错误,需要处理但不致命
18    Error,
19    /// 致命错误,必须停止
20    Fatal,
21}
22
23/// 错误分类
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum ErrorCategory {
26    /// 文件系统相关
27    FileSystem,
28    /// 网络相关
29    Network,
30    /// 解析相关
31    Parsing,
32    /// 验证相关
33    Validation,
34    /// 内部错误
35    Internal,
36}
37
38/// 错误上下文信息
39#[derive(Debug, Clone, Default)]
40pub struct ErrorContext {
41    pub file_path: Option<String>,
42    pub line_number: Option<u32>,
43    pub element_path: Option<String>,
44    pub operation: Option<String>,
45}
46
47/// 错误码,便于程序化处理
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49pub enum ErrorCode {
50    // 文件系统错误 (1000-1999)
51    FileNotFound = 1001,
52    FilePermissionDenied = 1002,
53    FileCorrupted = 1003,
54    FileInvalidFormat = 1004,
55
56    // IO错误 (1100-1199)
57    IoError = 1100,
58    ZipError = 1101,
59
60    // 解析错误 (2000-2999)
61    XmlParseError = 2001,
62    JsonParseError = 2002,
63    EncodingError = 2003,
64    MissingElement = 2004,
65    InvalidAttribute = 2005,
66    UnsupportedVersion = 2006,
67    InvalidNamespace = 2007,
68
69    // Excel错误 (3000-3999)
70    XlsxWorksheetNotFound = 3001,
71    XlsxInvalidCellReference = 3002,
72    XlsxInvalidFormula = 3003,
73    XlsxInvalidStyleId = 3004,
74    XlsxSharedStringIndexOutOfRange = 3005,
75
76    // Word错误 (4000-4999)
77    DocxStyleNotFound = 4001,
78    DocxInvalidTableStructure = 4002,
79    DocxInvalidParagraphFormat = 4003,
80    DocxBookmarkNotFound = 4004,
81
82    // PowerPoint错误 (5000-5999)
83    PptxSlideNotFound = 5001,
84    PptxLayoutNotFound = 5002,
85    PptxInvalidShapeId = 5003,
86    PptxInvalidAnimation = 5004,
87
88    // 通用错误 (9000-9999)
89    FormatError = 9001,
90    UnsupportedFormat = 9002,
91    StructureError = 9003,
92    OtherError = 9999,
93
94    // 通用分类错误码
95    FileError = 1000,
96    ParseError = 2000,
97    XlsxError = 3000,
98    DocxError = 4000,
99    PptxError = 5000,
100}
101
102/// 错误恢复建议
103#[derive(Debug, Clone)]
104pub struct RecoverySuggestion {
105    pub message: String,
106    pub action: RecoveryAction,
107}
108
109#[derive(Debug, Clone)]
110pub enum RecoveryAction {
111    /// 重试操作
112    Retry,
113    /// 使用默认值
114    UseDefault,
115    /// 跳过当前元素
116    SkipElement,
117    /// 需要用户输入
118    UserInput(String),
119    /// 无法恢复
120    None,
121}
122
123/// 带上下文的文档错误类型
124#[derive(Error, Debug)]
125pub enum OfficeError {
126    /// IO错误
127    #[error("IO错误: {0}")]
128    Io(#[from] io::Error),
129
130    /// ZIP文件错误
131    #[error("ZIP文件错误: {0}")]
132    Zip(#[from] ZipError),
133
134    /// XML解析错误
135    #[error("XML解析错误: {0}")]
136    Xml(#[from] XmlError),
137
138    /// JSON序列化错误
139    #[error("JSON错误: {0}")]
140    Json(#[from] JsonError),
141
142    /// 编码错误
143    #[error("编码错误: {0}")]
144    Encoding(#[from] std::string::FromUtf8Error),
145
146    /// 文件相关错误
147    #[error("文件错误: {0}")]
148    File(#[from] FileError),
149
150    /// 解析相关错误
151    #[error("解析错误: {0}")]
152    Parse(#[from] ParseError),
153
154    /// Excel特定错误
155    #[error("Excel错误: {0}")]
156    Xlsx(#[from] XlsxError),
157
158    /// Word特定错误
159    #[error("Word错误: {0}")]
160    Docx(#[from] DocxError),
161
162    /// PowerPoint特定错误
163    #[error("PowerPoint错误: {0}")]
164    Pptx(#[from] PptxError),
165
166    /// 格式错误
167    #[error("格式错误: {0}")]
168    Format(String),
169
170    /// 不支持的文件类型
171    #[error("不支持的文件类型: {0}")]
172    UnsupportedFormat(String),
173
174    /// 文档结构错误
175    #[error("文档结构错误: {0}")]
176    Structure(String),
177
178    /// 其他错误
179    #[error("其他错误: {0}")]
180    Other(String),
181
182    /// 带上下文的错误
183    #[error("{error}\n上下文: {context:?}")]
184    WithContext {
185        error: Box<OfficeError>,
186        context: ErrorContext,
187    },
188}
189
190/// 文件相关错误
191#[derive(Error, Debug)]
192pub enum FileError {
193    #[error("文件不存在: {path}")] NotFound {
194        path: String,
195    },
196
197    #[error("文件权限不足: {path}")] PermissionDenied {
198        path: String,
199    },
200
201    #[error("文件已损坏: {path}")] Corrupted {
202        path: String,
203    },
204
205    #[error("文件格式不正确: {path}")] InvalidFormat {
206        path: String,
207    },
208}
209
210/// 解析相关错误
211#[derive(Error, Debug)]
212pub enum ParseError {
213    #[error("缺少必需元素: {element}")] MissingElement {
214        element: String,
215    },
216
217    #[error("无效的属性值: {attribute}={value}")] InvalidAttribute {
218        attribute: String,
219        value: String,
220    },
221
222    #[error("不支持的版本: {version}")] UnsupportedVersion {
223        version: String,
224    },
225
226    #[error("无效的命名空间: {namespace}")] InvalidNamespace {
227        namespace: String,
228    },
229}
230
231/// Excel特定错误
232#[derive(Error, Debug)]
233pub enum XlsxError {
234    #[error("工作表不存在: {name}")] WorksheetNotFound {
235        name: String,
236    },
237
238    #[error("单元格引用无效: {reference}")] InvalidCellReference {
239        reference: String,
240    },
241
242    #[error("公式语法错误: {formula}")] InvalidFormula {
243        formula: String,
244    },
245
246    #[error("样式ID无效: {style_id}")] InvalidStyleId {
247        style_id: String,
248    },
249
250    #[error("共享字符串索引超出范围: {index}")] SharedStringIndexOutOfRange {
251        index: usize,
252    },
253
254    #[error("工作表创建失败: {name}")] WorksheetCreationFailed {
255        name: String,
256    },
257}
258
259/// Word特定错误
260#[derive(Error, Debug)]
261pub enum DocxError {
262    #[error("样式不存在: {style_id}")] StyleNotFound {
263        style_id: String,
264    },
265
266    #[error("表格结构无效")]
267    InvalidTableStructure,
268
269    #[error("段落格式错误: {reason}")] InvalidParagraphFormat {
270        reason: String,
271    },
272
273    #[error("书签不存在: {bookmark}")] BookmarkNotFound {
274        bookmark: String,
275    },
276}
277
278/// PowerPoint特定错误
279#[derive(Error, Debug)]
280pub enum PptxError {
281    #[error("幻灯片不存在: {slide_id}")] SlideNotFound {
282        slide_id: String,
283    },
284
285    #[error("布局不存在: {layout_id}")] LayoutNotFound {
286        layout_id: String,
287    },
288
289    #[error("形状ID无效: {shape_id}")] InvalidShapeId {
290        shape_id: String,
291    },
292
293    #[error("动画配置错误: {reason}")] InvalidAnimation {
294        reason: String,
295    },
296}
297
298impl OfficeError {
299    /// 创建带上下文的文件未找到错误
300    pub fn file_not_found_with_context(path: String, context: ErrorContext) -> Self {
301        Self::File(FileError::NotFound { path }).with_context(context)
302    }
303
304    /// 创建带上下文的解析错误
305    pub fn parse_error_with_context(element: String, context: ErrorContext) -> Self {
306        Self::Parse(ParseError::MissingElement { element }).with_context(context)
307    }
308
309    /// 创建带上下文的Excel错误
310    pub fn xlsx_error_with_context(name: String, context: ErrorContext) -> Self {
311        Self::Xlsx(XlsxError::WorksheetNotFound { name }).with_context(context)
312    }
313
314    /// 记录错误到监控器
315    pub fn record(&self) -> &Self {
316        error_monitor().record_error(self);
317        self
318    }
319
320    /// 创建错误并自动记录
321    pub fn new_and_record(error: OfficeError) -> Self {
322        error_monitor().record_error(&error);
323        error
324    }
325    /// 获取错误严重程度
326    pub fn severity(&self) -> ErrorSeverity {
327        match self {
328            Self::Io(_) => ErrorSeverity::Fatal,
329            Self::Zip(_) => ErrorSeverity::Fatal,
330            Self::Xml(_) => ErrorSeverity::Error,
331            Self::Json(_) => ErrorSeverity::Error,
332            Self::Encoding(_) => ErrorSeverity::Error,
333            Self::File(FileError::NotFound { .. }) => ErrorSeverity::Fatal,
334            Self::File(FileError::PermissionDenied { .. }) => ErrorSeverity::Fatal,
335            Self::File(FileError::Corrupted { .. }) => ErrorSeverity::Fatal,
336            Self::File(FileError::InvalidFormat { .. }) => ErrorSeverity::Error,
337            Self::Parse(_) => ErrorSeverity::Error,
338            Self::Xlsx(_) => ErrorSeverity::Error,
339            Self::Docx(_) => ErrorSeverity::Error,
340            Self::Pptx(_) => ErrorSeverity::Error,
341            Self::Format(_) => ErrorSeverity::Error,
342            Self::UnsupportedFormat(_) => ErrorSeverity::Fatal,
343            Self::Structure(_) => ErrorSeverity::Warning,
344            Self::Other(_) => ErrorSeverity::Error,
345            Self::WithContext { error, .. } => error.severity(),
346        }
347    }
348
349    /// 获取错误分类
350    pub fn category(&self) -> ErrorCategory {
351        match self {
352            Self::Io(_) | Self::File(_) => ErrorCategory::FileSystem,
353            Self::Zip(_) => ErrorCategory::FileSystem,
354            Self::Xml(_) | Self::Json(_) | Self::Encoding(_) => ErrorCategory::Parsing,
355            Self::Parse(_) => ErrorCategory::Parsing,
356            Self::Xlsx(_) | Self::Docx(_) | Self::Pptx(_) => ErrorCategory::Validation,
357            Self::Format(_) | Self::Structure(_) => ErrorCategory::Validation,
358            Self::UnsupportedFormat(_) => ErrorCategory::Parsing,
359            Self::Other(_) => ErrorCategory::Internal,
360            Self::WithContext { error, .. } => error.category(),
361        }
362    }
363
364    /// 获取错误码
365    pub fn error_code(&self) -> ErrorCode {
366        match self {
367            Self::Io(_) => ErrorCode::IoError,
368            Self::Zip(_) => ErrorCode::ZipError,
369            Self::Xml(_) => ErrorCode::XmlParseError,
370            Self::Json(_) => ErrorCode::JsonParseError,
371            Self::Encoding(_) => ErrorCode::EncodingError,
372            Self::File(FileError::NotFound { .. }) => ErrorCode::FileNotFound,
373            Self::File(FileError::PermissionDenied { .. }) => ErrorCode::FilePermissionDenied,
374            Self::File(FileError::Corrupted { .. }) => ErrorCode::FileCorrupted,
375            Self::File(FileError::InvalidFormat { .. }) => ErrorCode::FileInvalidFormat,
376            Self::Parse(ParseError::MissingElement { .. }) => ErrorCode::MissingElement,
377            Self::Parse(ParseError::InvalidAttribute { .. }) => ErrorCode::InvalidAttribute,
378            Self::Parse(ParseError::UnsupportedVersion { .. }) => ErrorCode::UnsupportedVersion,
379            Self::Parse(ParseError::InvalidNamespace { .. }) => ErrorCode::InvalidNamespace,
380            Self::Xlsx(XlsxError::WorksheetNotFound { .. }) => ErrorCode::XlsxWorksheetNotFound,
381            Self::Xlsx(XlsxError::InvalidCellReference { .. }) => {
382                ErrorCode::XlsxInvalidCellReference
383            }
384            Self::Xlsx(XlsxError::InvalidFormula { .. }) => ErrorCode::XlsxInvalidFormula,
385            Self::Xlsx(XlsxError::InvalidStyleId { .. }) => ErrorCode::XlsxInvalidStyleId,
386            Self::Xlsx(XlsxError::SharedStringIndexOutOfRange { .. }) => {
387                ErrorCode::XlsxSharedStringIndexOutOfRange
388            }
389            Self::Xlsx(XlsxError::WorksheetCreationFailed { .. }) => ErrorCode::XlsxError,
390            Self::Docx(DocxError::StyleNotFound { .. }) => ErrorCode::DocxStyleNotFound,
391            Self::Docx(DocxError::InvalidTableStructure) => ErrorCode::DocxInvalidTableStructure,
392            Self::Docx(DocxError::InvalidParagraphFormat { .. }) => {
393                ErrorCode::DocxInvalidParagraphFormat
394            }
395            Self::Docx(DocxError::BookmarkNotFound { .. }) => ErrorCode::DocxBookmarkNotFound,
396            Self::Pptx(PptxError::SlideNotFound { .. }) => ErrorCode::PptxSlideNotFound,
397            Self::Pptx(PptxError::LayoutNotFound { .. }) => ErrorCode::PptxLayoutNotFound,
398            Self::Pptx(PptxError::InvalidShapeId { .. }) => ErrorCode::PptxInvalidShapeId,
399            Self::Pptx(PptxError::InvalidAnimation { .. }) => ErrorCode::PptxInvalidAnimation,
400            Self::Format(_) => ErrorCode::FormatError,
401            Self::UnsupportedFormat(_) => ErrorCode::UnsupportedFormat,
402            Self::Structure(_) => ErrorCode::StructureError,
403            Self::Other(_) => ErrorCode::OtherError,
404            Self::WithContext { error, .. } => error.error_code(),
405        }
406    }
407
408    /// 判断错误是否可恢复
409    pub fn is_recoverable(&self) -> bool {
410        matches!(self.severity(), ErrorSeverity::Warning | ErrorSeverity::Error)
411    }
412
413    /// 添加错误上下文
414    pub fn with_context(self, context: ErrorContext) -> Self {
415        let error_with_context = Self::WithContext {
416            error: Box::new(self),
417            context,
418        };
419        // 记录带上下文的错误
420        error_monitor().record_error(&error_with_context);
421        error_with_context
422    }
423
424    /// 获取错误上下文
425    pub fn context(&self) -> Option<&ErrorContext> {
426        match self {
427            Self::WithContext { context, .. } => Some(context),
428            _ => None,
429        }
430    }
431
432    /// 获取根错误(去除上下文包装)
433    pub fn root_error(&self) -> &OfficeError {
434        match self {
435            Self::WithContext { error, .. } => error.root_error(),
436            _ => self,
437        }
438    }
439
440    /// 获取错误恢复建议
441    pub fn recovery_suggestion(&self) -> Option<RecoverySuggestion> {
442        match self.root_error() {
443            Self::File(FileError::NotFound { path }) =>
444                Some(RecoverySuggestion {
445                    message: format!("检查文件路径是否正确: {}", path),
446                    action: RecoveryAction::UserInput("请提供正确的文件路径".to_string()),
447                }),
448            Self::File(FileError::PermissionDenied { path }) =>
449                Some(RecoverySuggestion {
450                    message: format!("检查文件权限: {}", path),
451                    action: RecoveryAction::UserInput("请确保有足够的文件访问权限".to_string()),
452                }),
453            Self::Xlsx(XlsxError::WorksheetNotFound { name }) =>
454                Some(RecoverySuggestion {
455                    message: format!("工作表 '{}' 不存在,可以使用默认工作表", name),
456                    action: RecoveryAction::UseDefault,
457                }),
458            Self::Xlsx(XlsxError::InvalidCellReference { reference }) =>
459                Some(RecoverySuggestion {
460                    message: format!("单元格引用 '{}' 无效,跳过此单元格", reference),
461                    action: RecoveryAction::SkipElement,
462                }),
463            Self::Docx(DocxError::StyleNotFound { style_id }) =>
464                Some(RecoverySuggestion {
465                    message: format!("样式 '{}' 不存在,使用默认样式", style_id),
466                    action: RecoveryAction::UseDefault,
467                }),
468            Self::Pptx(PptxError::SlideNotFound { slide_id }) =>
469                Some(RecoverySuggestion {
470                    message: format!("幻灯片 '{}' 不存在,跳过此幻灯片", slide_id),
471                    action: RecoveryAction::SkipElement,
472                }),
473            Self::Structure(_) =>
474                Some(RecoverySuggestion {
475                    message: "文档结构异常,但可以继续处理".to_string(),
476                    action: RecoveryAction::SkipElement,
477                }),
478            Self::Io(_) | Self::Zip(_) | Self::UnsupportedFormat(_) =>
479                Some(RecoverySuggestion {
480                    message: "致命错误,无法恢复".to_string(),
481                    action: RecoveryAction::None,
482                }),
483            _ =>
484                Some(RecoverySuggestion {
485                    message: "可以重试操作".to_string(),
486                    action: RecoveryAction::Retry,
487                }),
488        }
489    }
490
491    /// 获取错误链
492    pub fn error_chain(&self) -> Vec<&dyn std::error::Error> {
493        let mut chain = vec![self as &dyn std::error::Error];
494        let mut source = self.source();
495        while let Some(err) = source {
496            chain.push(err);
497            source = err.source();
498        }
499        chain
500    }
501
502    /// 获取根因错误
503    pub fn root_cause(&self) -> &dyn std::error::Error {
504        self.error_chain().into_iter().last().unwrap()
505    }
506}
507
508/// 错误统计信息
509#[derive(Debug, Clone)]
510pub struct ErrorStats {
511    pub error_code: ErrorCode,
512    pub count: u64,
513    pub first_occurrence: u64, // Unix timestamp
514    pub last_occurrence: u64, // Unix timestamp
515    pub severity: ErrorSeverity,
516    pub category: ErrorCategory,
517}
518
519/// 错误监控器
520#[derive(Debug)]
521pub struct ErrorMonitor {
522    stats: Mutex<HashMap<ErrorCode, ErrorStats>>,
523    total_errors: AtomicU64,
524}
525
526impl ErrorMonitor {
527    pub fn new() -> Self {
528        Self {
529            stats: Mutex::new(HashMap::new()),
530            total_errors: AtomicU64::new(0),
531        }
532    }
533
534    /// 记录错误
535    pub fn record_error(&self, error: &OfficeError) {
536        let error_code = error.error_code();
537        let severity = error.severity();
538        let category = error.category();
539        let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs();
540
541        self.total_errors.fetch_add(1, Ordering::Relaxed);
542
543        let mut stats = self.stats.lock().unwrap();
544        let entry = stats.entry(error_code).or_insert(ErrorStats {
545            error_code,
546            count: 0,
547            first_occurrence: now,
548            last_occurrence: now,
549            severity,
550            category,
551        });
552
553        entry.count += 1;
554        entry.last_occurrence = now;
555    }
556
557    /// 获取错误统计
558    pub fn get_stats(&self) -> Vec<ErrorStats> {
559        let stats = self.stats.lock().unwrap();
560        stats.values().cloned().collect()
561    }
562
563    /// 获取总错误数
564    pub fn total_errors(&self) -> u64 {
565        self.total_errors.load(Ordering::Relaxed)
566    }
567
568    /// 获取最常见的错误
569    pub fn most_common_errors(&self, limit: usize) -> Vec<ErrorStats> {
570        let mut stats = self.get_stats();
571        stats.sort_by(|a, b| b.count.cmp(&a.count));
572        stats.into_iter().take(limit).collect()
573    }
574
575    /// 清除统计信息
576    pub fn clear_stats(&self) {
577        let mut stats = self.stats.lock().unwrap();
578        stats.clear();
579        self.total_errors.store(0, Ordering::Relaxed);
580    }
581}
582
583/// 全局错误监控器
584static ERROR_MONITOR: OnceLock<ErrorMonitor> = OnceLock::new();
585
586/// 获取全局错误监控器
587pub fn error_monitor() -> &'static ErrorMonitor {
588    ERROR_MONITOR.get_or_init(|| ErrorMonitor::new())
589}
590
591/// 简化的结果类型
592pub type Result<T> = std::result::Result<T, OfficeError>;