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