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}