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}