Skip to main content

quick_xml/
errors.rs

1//! Error management module
2
3use crate::encoding::{Decoder, 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!(f, "an XML declaration must start with `version` attribute, but in starts with `{}`", attr)
156            }
157            Self::UnknownVersion => {
158                f.write_str("unknown XML version: either 1.0 or 1.1 is expected")
159            }
160            Self::MissingDoctypeName => {
161                f.write_str("`<!DOCTYPE>` declaration does not contain a name of a document type")
162            }
163            Self::MissingEndTag(tag) => write!(
164                f,
165                "start tag not closed: `</{}>` not found before end of input",
166                tag,
167            ),
168            Self::UnmatchedEndTag(tag) => {
169                write!(f, "close tag `</{}>` does not match any open tag", tag)
170            }
171            Self::MismatchedEndTag { expected, found } => write!(
172                f,
173                "expected `</{}>`, but `</{}>` was found",
174                expected, found,
175            ),
176            Self::DoubleHyphenInComment => {
177                f.write_str("forbidden string `--` was found in a comment")
178            }
179            Self::UnclosedReference => f.write_str(
180                "entity or character reference not closed: `;` not found before end of input",
181            ),
182        }
183    }
184}
185
186impl std::error::Error for IllFormedError {}
187
188////////////////////////////////////////////////////////////////////////////////////////////////////
189
190/// The error type used by this crate.
191#[derive(Clone, Debug)]
192pub enum Error {
193    /// XML document cannot be read from underlying source.
194    ///
195    /// Contains the reference-counted I/O error to make the error type `Clone`able.
196    Io(Arc<IoError>),
197    /// The document does not corresponds to the XML grammar.
198    Syntax(SyntaxError),
199    /// The document is not [well-formed](https://www.w3.org/TR/xml11/#dt-wellformed).
200    IllFormed(IllFormedError),
201    /// Attribute parsing error
202    InvalidAttr(AttrError),
203    /// Encoding error
204    Encoding(EncodingError),
205    /// Escape error
206    Escape(EscapeError),
207    /// Parsed XML has some namespace-related problems
208    Namespace(NamespaceError),
209}
210
211impl Error {
212    pub(crate) fn missed_end(name: QName, decoder: Decoder) -> Self {
213        match decoder.decode(name.as_ref()) {
214            Ok(name) => IllFormedError::MissingEndTag(name.into()).into(),
215            Err(err) => err.into(),
216        }
217    }
218}
219
220impl From<IoError> for Error {
221    /// Creates a new `Error::Io` from the given error
222    #[inline]
223    fn from(error: IoError) -> Error {
224        match error.kind() {
225            IoErrorKind::InvalidData => match error.downcast::<EncodingError>() {
226                Ok(err) => Self::Encoding(err),
227                Err(err) => Self::Io(Arc::new(err)),
228            },
229            _ => Self::Io(Arc::new(error)),
230        }
231    }
232}
233
234impl From<SyntaxError> for Error {
235    /// Creates a new `Error::Syntax` from the given error
236    #[inline]
237    fn from(error: SyntaxError) -> Self {
238        Self::Syntax(error)
239    }
240}
241
242impl From<IllFormedError> for Error {
243    /// Creates a new `Error::IllFormed` from the given error
244    #[inline]
245    fn from(error: IllFormedError) -> Self {
246        Self::IllFormed(error)
247    }
248}
249
250impl From<EncodingError> for Error {
251    /// Creates a new `Error::EncodingError` from the given error
252    #[inline]
253    fn from(error: EncodingError) -> Error {
254        Self::Encoding(error)
255    }
256}
257
258impl From<EscapeError> for Error {
259    /// Creates a new `Error::EscapeError` from the given error
260    #[inline]
261    fn from(error: EscapeError) -> Error {
262        Self::Escape(error)
263    }
264}
265
266impl From<AttrError> for Error {
267    #[inline]
268    fn from(error: AttrError) -> Self {
269        Self::InvalidAttr(error)
270    }
271}
272
273impl From<NamespaceError> for Error {
274    #[inline]
275    fn from(error: NamespaceError) -> Self {
276        Self::Namespace(error)
277    }
278}
279
280/// A specialized `Result` type where the error is hard-wired to [`Error`].
281pub type Result<T> = std::result::Result<T, Error>;
282
283impl fmt::Display for Error {
284    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
285        match self {
286            Self::Io(e) => write!(f, "I/O error: {}", e),
287            Self::Syntax(e) => write!(f, "syntax error: {}", e),
288            Self::IllFormed(e) => write!(f, "ill-formed document: {}", e),
289            Self::InvalidAttr(e) => write!(f, "error while parsing attribute: {}", e),
290            Self::Encoding(e) => e.fmt(f),
291            Self::Escape(e) => e.fmt(f),
292            Self::Namespace(e) => e.fmt(f),
293        }
294    }
295}
296
297impl std::error::Error for Error {
298    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
299        match self {
300            Self::Io(e) => Some(e),
301            Self::Syntax(e) => Some(e),
302            Self::IllFormed(e) => Some(e),
303            Self::InvalidAttr(e) => Some(e),
304            Self::Encoding(e) => Some(e),
305            Self::Escape(e) => Some(e),
306            Self::Namespace(e) => Some(e),
307        }
308    }
309}
310
311#[cfg(feature = "serialize")]
312pub mod serialize {
313    //! A module to handle serde (de)serialization errors
314
315    use super::*;
316    use crate::utils::write_byte_string;
317    use std::borrow::Cow;
318    #[cfg(feature = "overlapped-lists")]
319    use std::num::NonZeroUsize;
320    use std::str::Utf8Error;
321
322    /// (De)serialization error
323    #[derive(Clone, Debug)]
324    pub enum DeError {
325        /// Serde custom error
326        Custom(String),
327        /// Xml parsing error
328        InvalidXml(Error),
329        /// This error indicates an error in the [`Deserialize`](serde::Deserialize)
330        /// implementation when read a map or a struct: `MapAccess::next_value[_seed]`
331        /// was called before `MapAccess::next_key[_seed]`.
332        ///
333        /// You should check your types, that implements corresponding trait.
334        KeyNotRead,
335        /// Deserializer encounter a start tag with a specified name when it is
336        /// not expecting. This happens when you try to deserialize a primitive
337        /// value (numbers, strings, booleans) from an XML element.
338        UnexpectedStart(Vec<u8>),
339        /// The [`Reader`] produced [`Event::Eof`] when it is not expecting,
340        /// for example, after producing [`Event::Start`] but before corresponding
341        /// [`Event::End`].
342        ///
343        /// [`Reader`]: crate::reader::Reader
344        /// [`Event::Eof`]: crate::events::Event::Eof
345        /// [`Event::Start`]: crate::events::Event::Start
346        /// [`Event::End`]: crate::events::Event::End
347        UnexpectedEof,
348        /// Too many events were skipped while deserializing a sequence, event limit
349        /// exceeded. The limit was provided as an argument
350        #[cfg(feature = "overlapped-lists")]
351        TooManyEvents(NonZeroUsize),
352    }
353
354    impl fmt::Display for DeError {
355        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
356            match self {
357                Self::Custom(s) => f.write_str(s),
358                Self::InvalidXml(e) => e.fmt(f),
359                Self::KeyNotRead => f.write_str("invalid `Deserialize` implementation: `MapAccess::next_value[_seed]` was called before `MapAccess::next_key[_seed]`"),
360                Self::UnexpectedStart(e) => {
361                    f.write_str("unexpected `Event::Start(")?;
362                    write_byte_string(f, e)?;
363                    f.write_str(")`")
364                }
365                Self::UnexpectedEof => f.write_str("unexpected `Event::Eof`"),
366                #[cfg(feature = "overlapped-lists")]
367                Self::TooManyEvents(s) => write!(f, "deserializer buffered {} events, limit exceeded", s),
368            }
369        }
370    }
371
372    impl std::error::Error for DeError {
373        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
374            match self {
375                Self::InvalidXml(e) => Some(e),
376                _ => None,
377            }
378        }
379    }
380
381    impl serde::de::Error for DeError {
382        fn custom<T: fmt::Display>(msg: T) -> Self {
383            Self::Custom(msg.to_string())
384        }
385    }
386
387    impl From<Error> for DeError {
388        #[inline]
389        fn from(e: Error) -> Self {
390            Self::InvalidXml(e)
391        }
392    }
393
394    impl From<EscapeError> for DeError {
395        #[inline]
396        fn from(e: EscapeError) -> Self {
397            Self::InvalidXml(e.into())
398        }
399    }
400
401    impl From<EncodingError> for DeError {
402        #[inline]
403        fn from(e: EncodingError) -> Self {
404            Self::InvalidXml(e.into())
405        }
406    }
407
408    impl From<AttrError> for DeError {
409        #[inline]
410        fn from(e: AttrError) -> Self {
411            Self::InvalidXml(e.into())
412        }
413    }
414
415    /// Serialization error
416    #[derive(Clone, Debug)]
417    pub enum SeError {
418        /// Serde custom error
419        Custom(String),
420        /// XML document cannot be written to underlying source.
421        ///
422        /// Contains the reference-counted I/O error to make the error type `Clone`able.
423        Io(Arc<IoError>),
424        /// Some value could not be formatted
425        Fmt(std::fmt::Error),
426        /// Serialized type cannot be represented in an XML due to violation of the
427        /// XML rules in the final XML document. For example, attempt to serialize
428        /// a `HashMap<{integer}, ...>` would cause this error because [XML name]
429        /// cannot start from a digit or a hyphen (minus sign). The same result
430        /// would occur if map key is a complex type that cannot be serialized as
431        /// a primitive type (i.e. string, char, bool, unit struct or unit variant).
432        ///
433        /// [XML name]: https://www.w3.org/TR/xml11/#sec-common-syn
434        Unsupported(Cow<'static, str>),
435        /// Some value could not be turned to UTF-8
436        NonEncodable(Utf8Error),
437    }
438
439    impl fmt::Display for SeError {
440        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
441            match self {
442                Self::Custom(s) => f.write_str(s),
443                Self::Io(e) => write!(f, "I/O error: {}", e),
444                Self::Fmt(e) => write!(f, "formatting error: {}", e),
445                Self::Unsupported(s) => write!(f, "unsupported value: {}", s),
446                Self::NonEncodable(e) => write!(f, "malformed UTF-8: {}", e),
447            }
448        }
449    }
450
451    impl ::std::error::Error for SeError {
452        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
453            match self {
454                Self::Io(e) => Some(e),
455                _ => None,
456            }
457        }
458    }
459
460    impl serde::ser::Error for SeError {
461        fn custom<T: fmt::Display>(msg: T) -> Self {
462            Self::Custom(msg.to_string())
463        }
464    }
465
466    impl From<IoError> for SeError {
467        #[inline]
468        fn from(e: IoError) -> Self {
469            Self::Io(Arc::new(e))
470        }
471    }
472
473    impl From<Utf8Error> for SeError {
474        #[inline]
475        fn from(e: Utf8Error) -> Self {
476            Self::NonEncodable(e)
477        }
478    }
479
480    impl From<fmt::Error> for SeError {
481        #[inline]
482        fn from(e: fmt::Error) -> Self {
483            Self::Fmt(e)
484        }
485    }
486}