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