quick_xml/errors.rs
1//! Error management module
2
3use crate::encoding::EncodingError;
4use crate::escape::EscapeError;
5use crate::events::attributes::AttrError;
6use crate::name::{NamespaceError, QName};
7use std::fmt;
8use std::io::{Error as IoError, ErrorKind as IoErrorKind};
9use 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)]
16pub 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
52impl fmt::Display for SyntaxError {
53 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54 match self {
55 Self::InvalidBangMarkup => f.write_str("unknown or missed symbol in markup"),
56 Self::UnclosedPI => {
57 f.write_str("processing instruction not closed: `?>` not found before end of input")
58 }
59 Self::UnclosedXmlDecl => {
60 f.write_str("XML declaration not closed: `?>` not found before end of input")
61 }
62 Self::UnclosedComment => {
63 f.write_str("comment not closed: `-->` not found before end of input")
64 }
65 Self::UnclosedDoctype => {
66 f.write_str("DOCTYPE not closed: `>` not found before end of input")
67 }
68 Self::UnclosedCData => {
69 f.write_str("CDATA not closed: `]]>` not found before end of input")
70 }
71 Self::UnclosedTag => f.write_str("tag not closed: `>` not found before end of input"),
72 Self::UnclosedSingleQuotedAttributeValue => {
73 f.write_str("attribute value not closed: `'` not found before end of input")
74 }
75 Self::UnclosedDoubleQuotedAttributeValue => {
76 f.write_str("attribute value not closed: `\"` not found before end of input")
77 }
78 }
79 }
80}
81
82impl 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)]
94pub 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
148impl fmt::Display for IllFormedError {
149 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
150 match self {
151 Self::MissingDeclVersion(None) => {
152 f.write_str("an XML declaration does not contain `version` attribute")
153 }
154 Self::MissingDeclVersion(Some(attr)) => {
155 write!(
156 f,
157 "an XML declaration must start with `version` attribute, but in starts with `{}`",
158 attr
159 )
160 }
161 Self::UnknownVersion => {
162 f.write_str("unknown XML version: either 1.0 or 1.1 is expected")
163 }
164 Self::MissingDoctypeName => {
165 f.write_str("`<!DOCTYPE>` declaration does not contain a name of a document type")
166 }
167 Self::MissingEndTag(tag) => write!(
168 f,
169 "start tag not closed: `</{}>` not found before end of input",
170 tag,
171 ),
172 Self::UnmatchedEndTag(tag) => {
173 write!(f, "close tag `</{}>` does not match any open tag", tag)
174 }
175 Self::MismatchedEndTag { expected, found } => write!(
176 f,
177 "expected `</{}>`, but `</{}>` was found",
178 expected, found,
179 ),
180 Self::DoubleHyphenInComment => {
181 f.write_str("forbidden string `--` was found in a comment")
182 }
183 Self::UnclosedReference => f.write_str(
184 "entity or character reference not closed: `;` not found before end of input",
185 ),
186 }
187 }
188}
189
190impl std::error::Error for IllFormedError {}
191
192////////////////////////////////////////////////////////////////////////////////////////////////////
193
194/// The error type used by this crate.
195#[derive(Clone, Debug)]
196pub 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
215impl Error {
216 pub(crate) fn missed_end(name: QName) -> Self {
217 IllFormedError::MissingEndTag(name.as_ref().to_string()).into()
218 }
219}
220
221impl From<IoError> for Error {
222 /// Creates a new `Error::Io` from the given error
223 #[inline]
224 fn from(error: IoError) -> Error {
225 match error.kind() {
226 IoErrorKind::InvalidData => match error.downcast::<EncodingError>() {
227 Ok(err) => Self::Encoding(err),
228 Err(err) => Self::Io(Arc::new(err)),
229 },
230 _ => Self::Io(Arc::new(error)),
231 }
232 }
233}
234
235impl From<SyntaxError> for Error {
236 /// Creates a new `Error::Syntax` from the given error
237 #[inline]
238 fn from(error: SyntaxError) -> Self {
239 Self::Syntax(error)
240 }
241}
242
243impl From<IllFormedError> for Error {
244 /// Creates a new `Error::IllFormed` from the given error
245 #[inline]
246 fn from(error: IllFormedError) -> Self {
247 Self::IllFormed(error)
248 }
249}
250
251impl 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
259impl From<std::str::Utf8Error> for Error {
260 #[inline]
261 fn from(error: std::str::Utf8Error) -> Error {
262 Self::Encoding(EncodingError::Utf8(error))
263 }
264}
265
266impl From<EscapeError> for Error {
267 /// Creates a new `Error::EscapeError` from the given error
268 #[inline]
269 fn from(error: EscapeError) -> Error {
270 Self::Escape(error)
271 }
272}
273
274impl From<AttrError> for Error {
275 #[inline]
276 fn from(error: AttrError) -> Self {
277 Self::InvalidAttr(error)
278 }
279}
280
281impl From<NamespaceError> for Error {
282 #[inline]
283 fn from(error: NamespaceError) -> Self {
284 Self::Namespace(error)
285 }
286}
287
288/// A specialized `Result` type where the error is hard-wired to [`Error`].
289pub type Result<T> = std::result::Result<T, Error>;
290
291impl fmt::Display for Error {
292 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
293 match self {
294 Self::Io(e) => write!(f, "I/O error: {}", e),
295 Self::Syntax(e) => write!(f, "syntax error: {}", e),
296 Self::IllFormed(e) => write!(f, "ill-formed document: {}", e),
297 Self::InvalidAttr(e) => write!(f, "error while parsing attribute: {}", e),
298 Self::Encoding(e) => e.fmt(f),
299 Self::Escape(e) => e.fmt(f),
300 Self::Namespace(e) => e.fmt(f),
301 }
302 }
303}
304
305impl std::error::Error for Error {
306 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
307 match self {
308 Self::Io(e) => Some(e),
309 Self::Syntax(e) => Some(e),
310 Self::IllFormed(e) => Some(e),
311 Self::InvalidAttr(e) => Some(e),
312 Self::Encoding(e) => Some(e),
313 Self::Escape(e) => Some(e),
314 Self::Namespace(e) => Some(e),
315 }
316 }
317}
318
319#[cfg(feature = "serialize")]
320pub 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}