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}