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