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}