vexy_vsvg/parser/
error.rs1use std::fmt;
4
5use thiserror::Error;
6
7#[derive(Error, Debug)]
9pub enum ParseError {
10 #[error("XML parsing error: {0}")]
11 XmlError(#[from] quick_xml::Error),
12
13 #[error("Attribute parsing error: {0}")]
14 AttrError(String),
15
16 #[error("Invalid UTF-8: {0}")]
17 Utf8Error(#[from] std::str::Utf8Error),
18
19 #[error("Document structure error: {0}")]
20 StructureError(String),
21
22 #[error("Unexpected end of document")]
23 UnexpectedEnd,
24
25 #[error("Invalid entity reference: {0}")]
26 EntityError(String),
27
28 #[error("Invalid namespace declaration: {0}")]
29 NamespaceError(String),
30
31 #[error("Malformed DOCTYPE: {0}")]
32 DoctypeError(String),
33
34 #[error("Unsupported XML feature: {0}")]
35 UnsupportedFeature(String),
36
37 #[error("Security violation: {0}")]
38 SecurityError(String),
39
40 #[error("File I/O error: {0}")]
41 FileIoError(#[from] std::io::Error),
42
43 #[error("{0}")]
44 DetailedError(Box<DetailedParseError>),
45}
46
47#[derive(Debug, Clone)]
49pub struct DetailedParseError {
50 pub file_path: Option<String>,
52 pub line: usize,
54 pub column: usize,
56 pub byte_offset: usize,
58 pub message: String,
60 pub severity: ErrorSeverity,
62 pub category: ErrorCategory,
64 pub context: Option<ErrorContext>,
66 pub suggestions: Vec<String>,
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72pub enum ErrorSeverity {
73 Error,
74 Warning,
75 Info,
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub enum ErrorCategory {
81 Syntax,
82 Structure,
83 Namespace,
84 Entity,
85 Encoding,
86 Security,
87 Performance,
88}
89
90#[derive(Debug, Clone)]
92pub struct ErrorContext {
93 pub lines: Vec<String>,
95 pub error_line_index: usize,
97 pub error_column: usize,
99}
100
101impl fmt::Display for DetailedParseError {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 write!(f, "{:?}: ", self.severity)?;
105
106 if let Some(ref path) = self.file_path {
107 write!(f, "{path}:")?;
108 }
109 write!(
110 f,
111 "{}:{}: {:?}: {}",
112 self.line, self.column, self.category, self.message
113 )?;
114
115 if let Some(ref ctx) = self.context {
117 writeln!(f)?;
118 writeln!(f)?;
119
120 let start_line = self.line.saturating_sub(ctx.error_line_index);
122 for (i, line) in ctx.lines.iter().enumerate() {
123 let line_num = start_line + i;
124 let prefix = if i == ctx.error_line_index { ">" } else { " " };
125 writeln!(f, "{prefix} {line_num:3} | {line}")?;
126
127 if i == ctx.error_line_index {
129 let spaces = " ".repeat(ctx.error_column + 6);
130 writeln!(f, "{spaces} ^")?;
131 }
132 }
133 }
134
135 if !self.suggestions.is_empty() {
137 writeln!(f)?;
138 writeln!(f, "Suggestions:")?;
139 for suggestion in &self.suggestions {
140 writeln!(f, " - {suggestion}")?;
141 }
142 }
143
144 Ok(())
145 }
146}
147
148pub type ParseResult<T> = Result<T, ParseError>;