/src/quick-xml/src/errors.rs
Line | Count | Source |
1 | | //! Error management module |
2 | | |
3 | | use crate::encoding::EncodingError; |
4 | | use crate::escape::EscapeError; |
5 | | use crate::events::attributes::AttrError; |
6 | | use crate::name::{NamespaceError, QName}; |
7 | | use std::fmt; |
8 | | use std::io::{Error as IoError, ErrorKind as IoErrorKind}; |
9 | | use std::sync::Arc; |
10 | | |
11 | | /// An error returned if parsed document does not correspond to the XML grammar, |
12 | | /// for example, a tag opened by `<` not closed with `>`. This error does not |
13 | | /// represent invalid XML constructs, for example, tags `<>` and `</>` a well-formed |
14 | | /// from syntax point-of-view. |
15 | | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
16 | | pub enum SyntaxError { |
17 | | /// The parser started to parse `<!`, but the input ended before it can recognize |
18 | | /// anything. |
19 | | InvalidBangMarkup, |
20 | | /// The parser started to parse processing instruction (`<?`), |
21 | | /// but the input ended before the `?>` sequence was found. |
22 | | UnclosedPI, |
23 | | /// The parser started to parse XML declaration (`<?xml` followed by `\t`, `\r`, `\n`, ` ` or `?`), |
24 | | /// but the input ended before the `?>` sequence was found. |
25 | | UnclosedXmlDecl, |
26 | | /// The parser started to parse comment (`<!--`) content, but the input ended |
27 | | /// before the `-->` sequence was found. |
28 | | UnclosedComment, |
29 | | /// The parser started to parse DTD (`<!DOCTYPE`) content, but the input ended |
30 | | /// before the closing `>` character was found. |
31 | | UnclosedDoctype, |
32 | | /// The parser started to parse `<![CDATA[` content, but the input ended |
33 | | /// before the `]]>` sequence was found. |
34 | | UnclosedCData, |
35 | | /// The parser started to parse tag content, but the input ended |
36 | | /// before the closing `>` character was found. |
37 | | UnclosedTag, |
38 | | /// The parser started to parse tag content and currently inside of a quoted string |
39 | | /// (i.e. in an attribute value), but the input ended before the closing quote was found. |
40 | | /// |
41 | | /// Note, that currently error location will point to a start of a tag (the `<` character) |
42 | | /// instead of a start of an attribute value. |
43 | | UnclosedSingleQuotedAttributeValue, |
44 | | /// The parser started to parse tag content and currently inside of a quoted string |
45 | | /// (i.e. in an attribute value), but the input ended before the closing quote was found. |
46 | | /// |
47 | | /// Note, that currently error location will point to a start of a tag (the `<` character) |
48 | | /// instead of a start of an attribute value. |
49 | | UnclosedDoubleQuotedAttributeValue, |
50 | | } |
51 | | |
52 | | impl fmt::Display for SyntaxError { |
53 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
54 | 0 | match self { |
55 | 0 | Self::InvalidBangMarkup => f.write_str("unknown or missed symbol in markup"), |
56 | | Self::UnclosedPI => { |
57 | 0 | f.write_str("processing instruction not closed: `?>` not found before end of input") |
58 | | } |
59 | | Self::UnclosedXmlDecl => { |
60 | 0 | f.write_str("XML declaration not closed: `?>` not found before end of input") |
61 | | } |
62 | | Self::UnclosedComment => { |
63 | 0 | f.write_str("comment not closed: `-->` not found before end of input") |
64 | | } |
65 | | Self::UnclosedDoctype => { |
66 | 0 | f.write_str("DOCTYPE not closed: `>` not found before end of input") |
67 | | } |
68 | | Self::UnclosedCData => { |
69 | 0 | f.write_str("CDATA not closed: `]]>` not found before end of input") |
70 | | } |
71 | 0 | Self::UnclosedTag => f.write_str("tag not closed: `>` not found before end of input"), |
72 | | Self::UnclosedSingleQuotedAttributeValue => { |
73 | 0 | f.write_str("attribute value not closed: `'` not found before end of input") |
74 | | } |
75 | | Self::UnclosedDoubleQuotedAttributeValue => { |
76 | 0 | f.write_str("attribute value not closed: `\"` not found before end of input") |
77 | | } |
78 | | } |
79 | 0 | } |
80 | | } |
81 | | |
82 | | impl std::error::Error for SyntaxError {} |
83 | | |
84 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
85 | | |
86 | | /// An error returned if parsed document is not [well-formed], for example, |
87 | | /// an opened tag is not closed before end of input. |
88 | | /// |
89 | | /// Those errors are not fatal: after encountering an error you can continue |
90 | | /// parsing the document. |
91 | | /// |
92 | | /// [well-formed]: https://www.w3.org/TR/xml11/#dt-wellformed |
93 | | #[derive(Clone, Debug, PartialEq, Eq)] |
94 | | pub enum IllFormedError { |
95 | | /// A `version` attribute was not found in an XML declaration or is not the |
96 | | /// first attribute. |
97 | | /// |
98 | | /// According to the [specification], the XML declaration (`<?xml ?>`) MUST contain |
99 | | /// a `version` attribute and it MUST be the first attribute. This error indicates, |
100 | | /// that the declaration does not contain attributes at all (if contains `None`) |
101 | | /// or either `version` attribute is not present or not the first attribute in |
102 | | /// the declaration. In the last case it contains the name of the found attribute. |
103 | | /// |
104 | | /// [specification]: https://www.w3.org/TR/xml11/#sec-prolog-dtd |
105 | | MissingDeclVersion(Option<String>), |
106 | | /// XML version specified in the declaration neither 1.0 or 1.1. |
107 | | UnknownVersion, |
108 | | /// A document type definition (DTD) does not contain a name of a root element. |
109 | | /// |
110 | | /// According to the [specification], document type definition (`<!DOCTYPE foo>`) |
111 | | /// MUST contain a name which defines a document type (`foo`). If that name |
112 | | /// is missed, this error is returned. |
113 | | /// |
114 | | /// [specification]: https://www.w3.org/TR/xml11/#NT-doctypedecl |
115 | | MissingDoctypeName, |
116 | | /// The end tag was not found during reading of a sub-tree of elements due to |
117 | | /// encountering an EOF from the underlying reader. This error is returned from |
118 | | /// [`Reader::read_to_end`]. |
119 | | /// |
120 | | /// [`Reader::read_to_end`]: crate::reader::Reader::read_to_end |
121 | | MissingEndTag(String), |
122 | | /// The specified end tag was encountered without corresponding open tag at the |
123 | | /// same level of hierarchy |
124 | | UnmatchedEndTag(String), |
125 | | /// The specified end tag does not match the start tag at that nesting level. |
126 | | MismatchedEndTag { |
127 | | /// Name of open tag, that is expected to be closed |
128 | | expected: String, |
129 | | /// Name of actually closed tag |
130 | | found: String, |
131 | | }, |
132 | | /// A comment contains forbidden double-hyphen (`--`) sequence inside. |
133 | | /// |
134 | | /// According to the [specification], for compatibility, comments MUST NOT contain |
135 | | /// double-hyphen (`--`) sequence, in particular, they cannot end by `--->`. |
136 | | /// |
137 | | /// The quick-xml by default does not check that, because this restriction is |
138 | | /// mostly artificial, but you can enable it in the [configuration]. |
139 | | /// |
140 | | /// [specification]: https://www.w3.org/TR/xml11/#sec-comments |
141 | | /// [configuration]: crate::reader::Config::check_comments |
142 | | DoubleHyphenInComment, |
143 | | /// The parser started to parse entity or character reference (`&...;`) in text, |
144 | | /// but the input ended before the closing `;` character was found. |
145 | | UnclosedReference, |
146 | | } |
147 | | |
148 | | impl fmt::Display for IllFormedError { |
149 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
150 | 0 | match self { |
151 | | Self::MissingDeclVersion(None) => { |
152 | 0 | f.write_str("an XML declaration does not contain `version` attribute") |
153 | | } |
154 | 0 | Self::MissingDeclVersion(Some(attr)) => { |
155 | 0 | write!( |
156 | 0 | f, |
157 | 0 | "an XML declaration must start with `version` attribute, but in starts with `{}`", |
158 | | attr |
159 | | ) |
160 | | } |
161 | | Self::UnknownVersion => { |
162 | 0 | f.write_str("unknown XML version: either 1.0 or 1.1 is expected") |
163 | | } |
164 | | Self::MissingDoctypeName => { |
165 | 0 | f.write_str("`<!DOCTYPE>` declaration does not contain a name of a document type") |
166 | | } |
167 | 0 | Self::MissingEndTag(tag) => write!( |
168 | 0 | f, |
169 | 0 | "start tag not closed: `</{}>` not found before end of input", |
170 | | tag, |
171 | | ), |
172 | 0 | Self::UnmatchedEndTag(tag) => { |
173 | 0 | write!(f, "close tag `</{}>` does not match any open tag", tag) |
174 | | } |
175 | 0 | Self::MismatchedEndTag { expected, found } => write!( |
176 | 0 | f, |
177 | 0 | "expected `</{}>`, but `</{}>` was found", |
178 | | expected, found, |
179 | | ), |
180 | | Self::DoubleHyphenInComment => { |
181 | 0 | f.write_str("forbidden string `--` was found in a comment") |
182 | | } |
183 | 0 | Self::UnclosedReference => f.write_str( |
184 | 0 | "entity or character reference not closed: `;` not found before end of input", |
185 | | ), |
186 | | } |
187 | 0 | } |
188 | | } |
189 | | |
190 | | impl std::error::Error for IllFormedError {} |
191 | | |
192 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
193 | | |
194 | | /// The error type used by this crate. |
195 | | #[derive(Clone, Debug)] |
196 | | pub enum Error { |
197 | | /// XML document cannot be read from underlying source. |
198 | | /// |
199 | | /// Contains the reference-counted I/O error to make the error type `Clone`able. |
200 | | Io(Arc<IoError>), |
201 | | /// The document does not corresponds to the XML grammar. |
202 | | Syntax(SyntaxError), |
203 | | /// The document is not [well-formed](https://www.w3.org/TR/xml11/#dt-wellformed). |
204 | | IllFormed(IllFormedError), |
205 | | /// Attribute parsing error |
206 | | InvalidAttr(AttrError), |
207 | | /// Encoding error |
208 | | Encoding(EncodingError), |
209 | | /// Escape error |
210 | | Escape(EscapeError), |
211 | | /// Parsed XML has some namespace-related problems |
212 | | Namespace(NamespaceError), |
213 | | } |
214 | | |
215 | | impl Error { |
216 | 0 | pub(crate) fn missed_end(name: QName) -> Self { |
217 | 0 | IllFormedError::MissingEndTag(name.as_ref().to_string()).into() |
218 | 0 | } |
219 | | } |
220 | | |
221 | | impl From<IoError> for Error { |
222 | | /// Creates a new `Error::Io` from the given error |
223 | | #[inline] |
224 | 0 | fn from(error: IoError) -> Error { |
225 | 0 | match error.kind() { |
226 | 0 | IoErrorKind::InvalidData => match error.downcast::<EncodingError>() { |
227 | 0 | Ok(err) => Self::Encoding(err), |
228 | 0 | Err(err) => Self::Io(Arc::new(err)), |
229 | | }, |
230 | 0 | _ => Self::Io(Arc::new(error)), |
231 | | } |
232 | 0 | } Unexecuted instantiation: <quick_xml::errors::Error as core::convert::From<std::io::error::Error>>::from Unexecuted instantiation: <quick_xml::errors::Error as core::convert::From<std::io::error::Error>>::from Unexecuted instantiation: <quick_xml::errors::Error as core::convert::From<std::io::error::Error>>::from Unexecuted instantiation: <quick_xml::errors::Error as core::convert::From<std::io::error::Error>>::from |
233 | | } |
234 | | |
235 | | impl From<SyntaxError> for Error { |
236 | | /// Creates a new `Error::Syntax` from the given error |
237 | | #[inline] |
238 | 179 | fn from(error: SyntaxError) -> Self { |
239 | 179 | Self::Syntax(error) |
240 | 179 | } |
241 | | } |
242 | | |
243 | | impl From<IllFormedError> for Error { |
244 | | /// Creates a new `Error::IllFormed` from the given error |
245 | | #[inline] |
246 | 0 | fn from(error: IllFormedError) -> Self { |
247 | 0 | Self::IllFormed(error) |
248 | 0 | } |
249 | | } |
250 | | |
251 | | impl From<EncodingError> for Error { |
252 | | /// Creates a new `Error::EncodingError` from the given error |
253 | | #[inline] |
254 | | fn from(error: EncodingError) -> Error { |
255 | | Self::Encoding(error) |
256 | | } |
257 | | } |
258 | | |
259 | | impl From<std::str::Utf8Error> for Error { |
260 | | #[inline] |
261 | 2.03k | fn from(error: std::str::Utf8Error) -> Error { |
262 | 2.03k | Self::Encoding(EncodingError::Utf8(error)) |
263 | 2.03k | } |
264 | | } |
265 | | |
266 | | impl From<EscapeError> for Error { |
267 | | /// Creates a new `Error::EscapeError` from the given error |
268 | | #[inline] |
269 | 21.7k | fn from(error: EscapeError) -> Error { |
270 | 21.7k | Self::Escape(error) |
271 | 21.7k | } |
272 | | } |
273 | | |
274 | | impl From<AttrError> for Error { |
275 | | #[inline] |
276 | 394 | fn from(error: AttrError) -> Self { |
277 | 394 | Self::InvalidAttr(error) |
278 | 394 | } |
279 | | } |
280 | | |
281 | | impl From<NamespaceError> for Error { |
282 | | #[inline] |
283 | 172 | fn from(error: NamespaceError) -> Self { |
284 | 172 | Self::Namespace(error) |
285 | 172 | } |
286 | | } |
287 | | |
288 | | /// A specialized `Result` type where the error is hard-wired to [`Error`]. |
289 | | pub type Result<T> = std::result::Result<T, Error>; |
290 | | |
291 | | impl fmt::Display for Error { |
292 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
293 | 0 | match self { |
294 | 0 | Self::Io(e) => write!(f, "I/O error: {}", e), |
295 | 0 | Self::Syntax(e) => write!(f, "syntax error: {}", e), |
296 | 0 | Self::IllFormed(e) => write!(f, "ill-formed document: {}", e), |
297 | 0 | Self::InvalidAttr(e) => write!(f, "error while parsing attribute: {}", e), |
298 | 0 | Self::Encoding(e) => e.fmt(f), |
299 | 0 | Self::Escape(e) => e.fmt(f), |
300 | 0 | Self::Namespace(e) => e.fmt(f), |
301 | | } |
302 | 0 | } |
303 | | } |
304 | | |
305 | | impl std::error::Error for Error { |
306 | 0 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
307 | 0 | match self { |
308 | 0 | Self::Io(e) => Some(e), |
309 | 0 | Self::Syntax(e) => Some(e), |
310 | 0 | Self::IllFormed(e) => Some(e), |
311 | 0 | Self::InvalidAttr(e) => Some(e), |
312 | 0 | Self::Encoding(e) => Some(e), |
313 | 0 | Self::Escape(e) => Some(e), |
314 | 0 | Self::Namespace(e) => Some(e), |
315 | | } |
316 | 0 | } |
317 | | } |
318 | | |
319 | | #[cfg(feature = "serialize")] |
320 | | pub mod serialize { |
321 | | //! A module to handle serde (de)serialization errors |
322 | | |
323 | | use super::*; |
324 | | use std::borrow::Cow; |
325 | | #[cfg(feature = "overlapped-lists")] |
326 | | use std::num::NonZeroUsize; |
327 | | use std::str::Utf8Error; |
328 | | |
329 | | /// (De)serialization error |
330 | | #[derive(Clone, Debug)] |
331 | | pub enum DeError { |
332 | | /// Serde custom error |
333 | | Custom(String), |
334 | | /// Xml parsing error |
335 | | InvalidXml(Error), |
336 | | /// This error indicates an error in the [`Deserialize`](serde::Deserialize) |
337 | | /// implementation when read a map or a struct: `MapAccess::next_value[_seed]` |
338 | | /// was called before `MapAccess::next_key[_seed]`. |
339 | | /// |
340 | | /// You should check your types, that implements corresponding trait. |
341 | | KeyNotRead, |
342 | | /// Deserializer encounter a start tag with a specified name when it is |
343 | | /// not expecting. This happens when you try to deserialize a primitive |
344 | | /// value (numbers, strings, booleans) from an XML element. |
345 | | MixedContent(String), |
346 | | /// The [`Reader`] produced [`Event::Eof`] when it is not expecting, |
347 | | /// for example, after producing [`Event::Start`] but before corresponding |
348 | | /// [`Event::End`]. |
349 | | /// |
350 | | /// [`Reader`]: crate::reader::Reader |
351 | | /// [`Event::Eof`]: crate::events::Event::Eof |
352 | | /// [`Event::Start`]: crate::events::Event::Start |
353 | | /// [`Event::End`]: crate::events::Event::End |
354 | | UnexpectedEof, |
355 | | /// The XML input exceeds the configured recursion limit. |
356 | | /// |
357 | | /// The contained value is the limit that was exceeded. This error is |
358 | | /// returned when deserializing deeply nested XML structures to prevent |
359 | | /// stack overflows. |
360 | | TooDeeplyNested(usize), |
361 | | /// Too many events were skipped while deserializing a sequence, event limit |
362 | | /// exceeded. The limit was provided as an argument |
363 | | #[cfg(feature = "overlapped-lists")] |
364 | | TooManyEvents(NonZeroUsize), |
365 | | } |
366 | | |
367 | | impl fmt::Display for DeError { |
368 | | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
369 | | match self { |
370 | | Self::Custom(s) => f.write_str(s), |
371 | | Self::InvalidXml(e) => e.fmt(f), |
372 | | Self::KeyNotRead => f.write_str("invalid `Deserialize` implementation: `MapAccess::next_value[_seed]` was called before `MapAccess::next_key[_seed]`"), |
373 | | Self::MixedContent(e) => write!(f, "cannot deserialize primitive type from mixed content, found unexpected tag <{}>", e), |
374 | | Self::UnexpectedEof => f.write_str("unexpected `Event::Eof`"), |
375 | | Self::TooDeeplyNested(limit) => write!(f, "XML is too deeply nested, recursion limit of {} exceeded", limit), |
376 | | #[cfg(feature = "overlapped-lists")] |
377 | | Self::TooManyEvents(s) => write!(f, "deserializer buffered {} events, limit exceeded", s), |
378 | | } |
379 | | } |
380 | | } |
381 | | |
382 | | impl std::error::Error for DeError { |
383 | | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
384 | | match self { |
385 | | Self::InvalidXml(e) => Some(e), |
386 | | _ => None, |
387 | | } |
388 | | } |
389 | | } |
390 | | |
391 | | impl serde::de::Error for DeError { |
392 | | fn custom<T: fmt::Display>(msg: T) -> Self { |
393 | | Self::Custom(msg.to_string()) |
394 | | } |
395 | | } |
396 | | |
397 | | impl From<Error> for DeError { |
398 | | #[inline] |
399 | | fn from(e: Error) -> Self { |
400 | | Self::InvalidXml(e) |
401 | | } |
402 | | } |
403 | | |
404 | | impl From<EscapeError> for DeError { |
405 | | #[inline] |
406 | | fn from(e: EscapeError) -> Self { |
407 | | Self::InvalidXml(e.into()) |
408 | | } |
409 | | } |
410 | | |
411 | | impl From<EncodingError> for DeError { |
412 | | #[inline] |
413 | | fn from(e: EncodingError) -> Self { |
414 | | Self::InvalidXml(e.into()) |
415 | | } |
416 | | } |
417 | | |
418 | | impl From<AttrError> for DeError { |
419 | | #[inline] |
420 | | fn from(e: AttrError) -> Self { |
421 | | Self::InvalidXml(e.into()) |
422 | | } |
423 | | } |
424 | | |
425 | | impl From<NamespaceError> for DeError { |
426 | | #[inline] |
427 | | fn from(e: NamespaceError) -> Self { |
428 | | Self::InvalidXml(e.into()) |
429 | | } |
430 | | } |
431 | | |
432 | | /// Serialization error |
433 | | #[derive(Clone, Debug)] |
434 | | pub enum SeError { |
435 | | /// Serde custom error |
436 | | Custom(String), |
437 | | /// XML document cannot be written to underlying source. |
438 | | /// |
439 | | /// Contains the reference-counted I/O error to make the error type `Clone`able. |
440 | | Io(Arc<IoError>), |
441 | | /// Some value could not be formatted |
442 | | Fmt(std::fmt::Error), |
443 | | /// Serialized type cannot be represented in an XML due to violation of the |
444 | | /// XML rules in the final XML document. For example, attempt to serialize |
445 | | /// a `HashMap<{integer}, ...>` would cause this error because [XML name] |
446 | | /// cannot start from a digit or a hyphen (minus sign). The same result |
447 | | /// would occur if map key is a complex type that cannot be serialized as |
448 | | /// a primitive type (i.e. string, char, bool, unit struct or unit variant). |
449 | | /// |
450 | | /// [XML name]: https://www.w3.org/TR/xml11/#sec-common-syn |
451 | | Unsupported(Cow<'static, str>), |
452 | | /// Some value could not be turned to UTF-8 |
453 | | NonEncodable(Utf8Error), |
454 | | } |
455 | | |
456 | | impl fmt::Display for SeError { |
457 | | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
458 | | match self { |
459 | | Self::Custom(s) => f.write_str(s), |
460 | | Self::Io(e) => write!(f, "I/O error: {}", e), |
461 | | Self::Fmt(e) => write!(f, "formatting error: {}", e), |
462 | | Self::Unsupported(s) => write!(f, "unsupported value: {}", s), |
463 | | Self::NonEncodable(e) => write!(f, "malformed UTF-8: {}", e), |
464 | | } |
465 | | } |
466 | | } |
467 | | |
468 | | impl ::std::error::Error for SeError { |
469 | | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
470 | | match self { |
471 | | Self::Io(e) => Some(e), |
472 | | _ => None, |
473 | | } |
474 | | } |
475 | | } |
476 | | |
477 | | impl serde::ser::Error for SeError { |
478 | | fn custom<T: fmt::Display>(msg: T) -> Self { |
479 | | Self::Custom(msg.to_string()) |
480 | | } |
481 | | } |
482 | | |
483 | | impl From<IoError> for SeError { |
484 | | #[inline] |
485 | | fn from(e: IoError) -> Self { |
486 | | Self::Io(Arc::new(e)) |
487 | | } |
488 | | } |
489 | | |
490 | | impl From<Utf8Error> for SeError { |
491 | | #[inline] |
492 | | fn from(e: Utf8Error) -> Self { |
493 | | Self::NonEncodable(e) |
494 | | } |
495 | | } |
496 | | |
497 | | impl From<fmt::Error> for SeError { |
498 | | #[inline] |
499 | | fn from(e: fmt::Error) -> Self { |
500 | | Self::Fmt(e) |
501 | | } |
502 | | } |
503 | | } |