Skip to main content

oxixml_xml/
errors.rs

1//! Error management for the crate.
2
3use crate::encoding::EncodingError;
4use crate::escape::EscapeError;
5use crate::events::attributes::AttrError;
6use crate::name::NamespaceError;
7use std::fmt;
8use std::io::{Error as IoError, ErrorKind as IoErrorKind};
9use std::sync::Arc;
10
11/// An error returned when a parsed document does not conform to the XML grammar,
12/// for example a tag opened by `<` that is not closed with `>`.
13///
14/// This error does not represent invalid XML *constructs* — for example the tags
15/// `<>` and `</>` are well-formed from a syntactic point of view.
16///
17/// Marked `#[non_exhaustive]` so that adding a new syntax diagnostic in a future
18/// release is not a breaking change for callers who match on this type.
19#[derive(Copy, Clone, Debug, PartialEq, Eq)]
20#[non_exhaustive]
21pub enum SyntaxError {
22    /// The parser started to parse `<!`, but the input ended before it could
23    /// recognize anything.
24    InvalidBangMarkup,
25    /// The parser started to parse a processing instruction (`<?`), but the input
26    /// ended before the `?>` sequence was found.
27    UnclosedPI,
28    /// The parser started to parse an XML declaration (`<?xml` followed by white
29    /// space or `?`), but the input ended before the `?>` sequence was found.
30    UnclosedXmlDecl,
31    /// The parser started to parse a comment (`<!--`), but the input ended before
32    /// the `-->` sequence was found.
33    UnclosedComment,
34    /// The parser started to parse a DTD (`<!DOCTYPE`), but the input ended before
35    /// the closing `>` character was found.
36    UnclosedDoctype,
37    /// The parser started to parse `<![CDATA[`, but the input ended before the
38    /// `]]>` sequence was found.
39    UnclosedCData,
40    /// The parser started to parse tag content, but the input ended before the
41    /// closing `>` character was found.
42    UnclosedTag,
43    /// The parser was inside a single-quoted attribute value, but the input ended
44    /// before the closing quote was found.
45    UnclosedSingleQuotedAttributeValue,
46    /// The parser was inside a double-quoted attribute value, but the input ended
47    /// before the closing quote was found.
48    UnclosedDoubleQuotedAttributeValue,
49}
50
51impl fmt::Display for SyntaxError {
52    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53        match self {
54            Self::InvalidBangMarkup => f.write_str("unknown or missed symbol in markup"),
55            Self::UnclosedPI => {
56                f.write_str("processing instruction not closed: `?>` not found before end of input")
57            }
58            Self::UnclosedXmlDecl => {
59                f.write_str("XML declaration not closed: `?>` not found before end of input")
60            }
61            Self::UnclosedComment => {
62                f.write_str("comment not closed: `-->` not found before end of input")
63            }
64            Self::UnclosedDoctype => {
65                f.write_str("DOCTYPE not closed: `>` not found before end of input")
66            }
67            Self::UnclosedCData => {
68                f.write_str("CDATA not closed: `]]>` not found before end of input")
69            }
70            Self::UnclosedTag => f.write_str("tag not closed: `>` not found before end of input"),
71            Self::UnclosedSingleQuotedAttributeValue => {
72                f.write_str("attribute value not closed: `'` not found before end of input")
73            }
74            Self::UnclosedDoubleQuotedAttributeValue => {
75                f.write_str("attribute value not closed: `\"` not found before end of input")
76            }
77        }
78    }
79}
80
81impl std::error::Error for SyntaxError {}
82
83////////////////////////////////////////////////////////////////////////////////////////////////////
84
85/// An error returned when a parsed document is not [well-formed], for example an
86/// opened tag that is not closed before the end of input.
87///
88/// These errors are not fatal: parsing can continue after encountering one.
89///
90/// Marked `#[non_exhaustive]` so that adding a new ill-formedness diagnostic in a
91/// future release is not a breaking change for callers who match on this type.
92///
93/// [well-formed]: https://www.w3.org/TR/xml11/#dt-wellformed
94#[derive(Clone, Debug, PartialEq, Eq)]
95#[non_exhaustive]
96pub enum IllFormedError {
97    /// A `version` attribute was not found in an XML declaration or is not the
98    /// first attribute. Contains the name of the found attribute, if any.
99    MissingDeclVersion(Option<String>),
100    /// The XML version specified in the declaration is neither 1.0 nor 1.1.
101    UnknownVersion,
102    /// A document type definition (`<!DOCTYPE foo>`) does not contain a name.
103    MissingDoctypeName,
104    /// An end tag was not found while reading a sub-tree of elements because an
105    /// EOF was encountered.
106    MissingEndTag(String),
107    /// An end tag was encountered without a corresponding open tag at the same
108    /// level of the hierarchy.
109    UnmatchedEndTag(String),
110    /// An end tag does not match the start tag at that nesting level.
111    MismatchedEndTag {
112        /// Name of the open tag that is expected to be closed.
113        expected: String,
114        /// Name of the tag that was actually closed.
115        found: String,
116    },
117    /// A comment contains the forbidden double-hyphen (`--`) sequence.
118    DoubleHyphenInComment,
119    /// The parser started to parse an entity or character reference (`&...;`), but
120    /// the input ended before the closing `;` character was found.
121    UnclosedReference,
122}
123
124impl fmt::Display for IllFormedError {
125    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
126        match self {
127            Self::MissingDeclVersion(None) => {
128                f.write_str("an XML declaration does not contain `version` attribute")
129            }
130            Self::MissingDeclVersion(Some(attr)) => {
131                write!(
132                    f,
133                    "an XML declaration must start with `version` attribute, but in starts with `{}`",
134                    attr
135                )
136            }
137            Self::UnknownVersion => {
138                f.write_str("unknown XML version: either 1.0 or 1.1 is expected")
139            }
140            Self::MissingDoctypeName => {
141                f.write_str("`<!DOCTYPE>` declaration does not contain a name of a document type")
142            }
143            Self::MissingEndTag(tag) => write!(
144                f,
145                "start tag not closed: `</{}>` not found before end of input",
146                tag,
147            ),
148            Self::UnmatchedEndTag(tag) => {
149                write!(f, "close tag `</{}>` does not match any open tag", tag)
150            }
151            Self::MismatchedEndTag { expected, found } => {
152                write!(
153                    f,
154                    "expected `</{}>`, but `</{}>` was found",
155                    expected, found
156                )
157            }
158            Self::DoubleHyphenInComment => {
159                f.write_str("forbidden string `--` was found in a comment")
160            }
161            Self::UnclosedReference => f.write_str(
162                "entity or character reference not closed: `;` not found before end of input",
163            ),
164        }
165    }
166}
167
168impl std::error::Error for IllFormedError {}
169
170////////////////////////////////////////////////////////////////////////////////////////////////////
171
172/// A violation of an XML [well-formedness constraint] that is reported only when
173/// [`Config::check_well_formedness`] is enabled.
174///
175/// The default parsing path of this crate is deliberately permissive so that it
176/// stays a drop-in replacement for `quick-xml`; enabling the flag turns the
177/// [`Reader`] into a conforming non-validating XML processor and these errors
178/// become observable.
179///
180/// [well-formedness constraint]: https://www.w3.org/TR/xml/#dt-wellformed
181/// [`Config::check_well_formedness`]: crate::reader::Config::check_well_formedness
182/// [`Reader`]: crate::reader::Reader
183#[derive(Clone, Debug, PartialEq, Eq)]
184#[non_exhaustive]
185pub enum WellFormedError {
186    /// A character that the [`Char`] production of the document's XML version
187    /// does not allow was found.
188    ///
189    /// [`Char`]: https://www.w3.org/TR/xml/#NT-Char
190    InvalidCharacter(char),
191    /// A token that must match the [`Name`] production does not.
192    ///
193    /// [`Name`]: https://www.w3.org/TR/xml/#NT-Name
194    InvalidName(String),
195    /// A public identifier contains a character outside the [`PubidChar`]
196    /// production.
197    ///
198    /// [`PubidChar`]: https://www.w3.org/TR/xml/#NT-PubidChar
199    InvalidPublicId(String),
200    /// A `&...;` entity or character reference does not match the [`Reference`]
201    /// production, or a character reference denotes a code point that is not a
202    /// legal XML character.
203    ///
204    /// [`Reference`]: https://www.w3.org/TR/xml/#NT-Reference
205    InvalidReference(String),
206    /// A general entity was referenced but never declared, violating the
207    /// [Entity Declared] constraint.
208    ///
209    /// [Entity Declared]: https://www.w3.org/TR/xml/#wf-entdeclared
210    UndeclaredEntity(String),
211    /// A general entity refers to itself, directly or indirectly, violating the
212    /// [No Recursion] constraint.
213    ///
214    /// [No Recursion]: https://www.w3.org/TR/xml/#norecursion
215    RecursiveEntity(String),
216    /// Expanding general entities exceeded the limits this processor imposes on
217    /// the total number of expansions or on the total size of the expanded text.
218    ///
219    /// Entity expansion is not recursive in the sense of the [No Recursion]
220    /// constraint when every entity has a different name, yet a document such as
221    /// the "billion laughs" bomb can still expand exponentially. Since a
222    /// non-validating processor is not required to expand entities without
223    /// bound, such a document is rejected instead of being expanded.
224    ///
225    /// [No Recursion]: https://www.w3.org/TR/xml/#norecursion
226    EntityExpansionLimit {
227        /// Name of the entity whose expansion exhausted the budget.
228        entity: String,
229        /// Description of the limit that was hit.
230        limit: &'static str,
231    },
232    /// An unparsed (`NDATA`) entity was referenced from content or from an
233    /// attribute value, violating the [Parsed Entity] constraint.
234    ///
235    /// [Parsed Entity]: https://www.w3.org/TR/xml/#wf-parsedent
236    UnparsedEntityReference(String),
237    /// The replacement text of a general entity is not well-formed on its own,
238    /// violating the [Legal Character] / balanced-markup constraints. Contains
239    /// the entity name and the nested failure.
240    IllFormedEntity {
241        /// Name of the entity whose replacement text is at fault.
242        entity: String,
243        /// Description of the problem found inside the replacement text.
244        reason: String,
245    },
246    /// An attribute value contains a literal `<`, violating the [No `<` in
247    /// Attribute Values] constraint. Contains the attribute name.
248    ///
249    /// [No `<` in Attribute Values]: https://www.w3.org/TR/xml/#CleanAttrVals
250    LessThanInAttributeValue(String),
251    /// An attribute value refers, directly or indirectly, to an external entity,
252    /// violating the [No External Entity References] constraint.
253    ///
254    /// [No External Entity References]: https://www.w3.org/TR/xml/#NoExternalRefs
255    ExternalEntityInAttributeValue(String),
256    /// The same attribute name appears twice on one tag, violating the
257    /// [Unique Att Spec] constraint.
258    ///
259    /// [Unique Att Spec]: https://www.w3.org/TR/xml/#uniqattspec
260    DuplicateAttribute(String),
261    /// A name does not match the [`QName`] production of Namespaces in XML, or a
262    /// name that must be an `NCName` contains a colon. Only reported while
263    /// namespace processing is active, that is for the
264    /// [`NsReader`](crate::reader::NsReader).
265    ///
266    /// [`QName`]: https://www.w3.org/TR/xml-names/#NT-QName
267    InvalidQName(String),
268    /// A namespace declaration binds a prefix to the empty namespace name, which
269    /// [Namespaces in XML 1.0] does not allow.
270    ///
271    /// [Namespaces in XML 1.0]: https://www.w3.org/TR/xml-names/#nsc-NSDeclared
272    EmptyPrefixBinding(String),
273    /// A start, end or empty-element tag does not match the [`STag`] production.
274    /// Contains a description of the expectation that was violated.
275    ///
276    /// [`STag`]: https://www.w3.org/TR/xml/#NT-STag
277    MalformedTag(String),
278    /// Character data contains the `]]>` sequence outside of a CDATA section.
279    CDataEndInContent,
280    /// A processing instruction uses the reserved target `xml` (in any case
281    /// combination).
282    ReservedPiTarget(String),
283    /// An XML declaration was found somewhere other than at the very beginning
284    /// of the document entity.
285    MisplacedXmlDeclaration,
286    /// The XML declaration does not match the [`XMLDecl`] production. Contains a
287    /// description of the expectation that was violated.
288    ///
289    /// [`XMLDecl`]: https://www.w3.org/TR/xml/#NT-XMLDecl
290    InvalidXmlDeclaration(String),
291    /// A `<!DOCTYPE>` declaration appears after the root element started, or a
292    /// second one appears.
293    MisplacedDoctype,
294    /// The document contains no root element.
295    MissingRootElement,
296    /// A second top-level element was found after the root element was closed.
297    MultipleRootElements,
298    /// Character data, a CDATA section or an entity reference was found outside
299    /// of the root element.
300    ContentOutsideRoot,
301    /// The document type declaration does not match the [`doctypedecl`]
302    /// production. Contains a description of the expectation that was violated.
303    ///
304    /// [`doctypedecl`]: https://www.w3.org/TR/xml/#NT-doctypedecl
305    InvalidDoctype(String),
306}
307
308impl fmt::Display for WellFormedError {
309    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
310        match self {
311            Self::InvalidCharacter(ch) => {
312                write!(
313                    f,
314                    "character U+{:04X} is not allowed by the `Char` production",
315                    *ch as u32
316                )
317            }
318            Self::InvalidName(name) => {
319                write!(f, "`{}` does not match the `Name` production", name)
320            }
321            Self::InvalidPublicId(id) => write!(
322                f,
323                "public identifier `{}` contains a character outside `PubidChar`",
324                id
325            ),
326            Self::InvalidReference(reference) => {
327                write!(
328                    f,
329                    "`{}` is not a valid entity or character reference",
330                    reference
331                )
332            }
333            Self::UndeclaredEntity(name) => {
334                write!(f, "reference to the undeclared entity `{}`", name)
335            }
336            Self::RecursiveEntity(name) => {
337                write!(f, "entity `{}` refers to itself", name)
338            }
339            Self::EntityExpansionLimit { entity, limit } => write!(
340                f,
341                "expanding the entity `{}` exceeds the {} of this processor",
342                entity, limit
343            ),
344            Self::UnparsedEntityReference(name) => write!(
345                f,
346                "reference to the unparsed (NDATA) entity `{}` is not allowed here",
347                name
348            ),
349            Self::IllFormedEntity { entity, reason } => write!(
350                f,
351                "replacement text of the entity `{}` is not well-formed: {}",
352                entity, reason
353            ),
354            Self::LessThanInAttributeValue(name) => write!(
355                f,
356                "value of the attribute `{}` contains a literal `<`",
357                name
358            ),
359            Self::ExternalEntityInAttributeValue(name) => write!(
360                f,
361                "an attribute value cannot refer to the external entity `{}`",
362                name
363            ),
364            Self::DuplicateAttribute(name) => {
365                write!(f, "the attribute `{}` is specified more than once", name)
366            }
367            Self::InvalidQName(name) => {
368                write!(f, "`{}` does not match the `QName` production", name)
369            }
370            Self::EmptyPrefixBinding(prefix) => write!(
371                f,
372                "the prefix `{}` cannot be bound to the empty namespace name",
373                prefix
374            ),
375            Self::MalformedTag(reason) => write!(f, "malformed tag: {}", reason),
376            Self::CDataEndInContent => {
377                f.write_str("the sequence `]]>` is not allowed in character data")
378            }
379            Self::ReservedPiTarget(target) => write!(
380                f,
381                "`{}` is a reserved processing instruction target",
382                target
383            ),
384            Self::MisplacedXmlDeclaration => f.write_str(
385                "the XML declaration must be at the very beginning of the document entity",
386            ),
387            Self::InvalidXmlDeclaration(reason) => {
388                write!(f, "malformed XML declaration: {}", reason)
389            }
390            Self::MisplacedDoctype => {
391                f.write_str("`<!DOCTYPE>` must appear once, before the root element")
392            }
393            Self::MissingRootElement => f.write_str("the document has no root element"),
394            Self::MultipleRootElements => {
395                f.write_str("the document has more than one root element")
396            }
397            Self::ContentOutsideRoot => {
398                f.write_str("character data is not allowed outside of the root element")
399            }
400            Self::InvalidDoctype(reason) => {
401                write!(f, "malformed document type declaration: {}", reason)
402            }
403        }
404    }
405}
406
407impl std::error::Error for WellFormedError {}
408
409////////////////////////////////////////////////////////////////////////////////////////////////////
410
411/// The error type used by this crate.
412///
413/// Marked `#[non_exhaustive]`: unlike `quick-xml` 0.41, this crate also reports
414/// optional well-formedness constraints (see [`NotWellFormed`](Self::NotWellFormed))
415/// and will keep growing past that upstream baseline, so a variant match must
416/// carry a wildcard arm to stay source-compatible across releases.
417#[derive(Clone, Debug)]
418#[non_exhaustive]
419pub enum Error {
420    /// An XML document cannot be read from the underlying source.
421    ///
422    /// Contains a reference-counted I/O error to make the error type `Clone`able.
423    Io(Arc<IoError>),
424    /// The document does not correspond to the XML grammar.
425    Syntax(SyntaxError),
426    /// The document is not [well-formed](https://www.w3.org/TR/xml11/#dt-wellformed).
427    IllFormed(IllFormedError),
428    /// The document violates a well-formedness constraint that is checked only
429    /// when [`Config::check_well_formedness`] is enabled.
430    ///
431    /// [`Config::check_well_formedness`]: crate::reader::Config::check_well_formedness
432    NotWellFormed(WellFormedError),
433    /// Attribute parsing error.
434    InvalidAttr(AttrError),
435    /// Encoding error.
436    Encoding(EncodingError),
437    /// Escape error.
438    Escape(EscapeError),
439    /// The parsed XML has some namespace-related problem.
440    Namespace(NamespaceError),
441}
442
443impl Error {
444    /// Builds the error returned when a [`read_to_end`] family method reaches
445    /// end of input before finding the closing tag for `name`.
446    ///
447    /// [`read_to_end`]: crate::reader::Reader::read_to_end
448    pub(crate) fn missed_end(name: crate::name::QName, decoder: crate::encoding::Decoder) -> Self {
449        match decoder.decode(name.as_ref()) {
450            Ok(name) => IllFormedError::MissingEndTag(name.into_owned()).into(),
451            Err(err) => err.into(),
452        }
453    }
454
455    /// Builds the error returned when the [serde] deserializer is handed a
456    /// closing tag with no corresponding open tag anywhere in the document.
457    ///
458    /// This can only happen when [`Config::allow_unmatched_ends`] lets the
459    /// reader emit such an [`Event::End`] instead of rejecting it; the
460    /// deserializer has no use for the event, so it is turned into the same
461    /// [`IllFormedError::UnmatchedEndTag`] the reader itself would report if
462    /// that option were off.
463    ///
464    /// [serde]: https://serde.rs/
465    /// [`Config::allow_unmatched_ends`]: crate::reader::Config::allow_unmatched_ends
466    /// [`Event::End`]: crate::events::Event::End
467    #[cfg(feature = "serialize")]
468    pub(crate) fn unmatched_end(
469        name: crate::name::QName,
470        decoder: crate::encoding::Decoder,
471    ) -> Self {
472        match decoder.decode(name.as_ref()) {
473            Ok(name) => IllFormedError::UnmatchedEndTag(name.into_owned()).into(),
474            Err(err) => err.into(),
475        }
476    }
477}
478
479impl From<IoError> for Error {
480    /// Creates a new [`Error::Io`] from the given error.
481    #[inline]
482    fn from(error: IoError) -> Error {
483        match error.kind() {
484            IoErrorKind::InvalidData => match error.downcast::<EncodingError>() {
485                Ok(err) => Self::Encoding(err),
486                Err(err) => Self::Io(Arc::new(err)),
487            },
488            _ => Self::Io(Arc::new(error)),
489        }
490    }
491}
492
493impl From<SyntaxError> for Error {
494    /// Creates a new [`Error::Syntax`] from the given error.
495    #[inline]
496    fn from(error: SyntaxError) -> Self {
497        Self::Syntax(error)
498    }
499}
500
501impl From<WellFormedError> for Error {
502    /// Creates a new [`Error::NotWellFormed`] from the given error.
503    #[inline]
504    fn from(error: WellFormedError) -> Self {
505        Self::NotWellFormed(error)
506    }
507}
508
509impl From<IllFormedError> for Error {
510    /// Creates a new [`Error::IllFormed`] from the given error.
511    #[inline]
512    fn from(error: IllFormedError) -> Self {
513        Self::IllFormed(error)
514    }
515}
516
517impl From<EncodingError> for Error {
518    /// Creates a new [`Error::Encoding`] from the given error.
519    #[inline]
520    fn from(error: EncodingError) -> Error {
521        Self::Encoding(error)
522    }
523}
524
525impl From<EscapeError> for Error {
526    /// Creates a new [`Error::Escape`] from the given error.
527    #[inline]
528    fn from(error: EscapeError) -> Error {
529        Self::Escape(error)
530    }
531}
532
533impl From<AttrError> for Error {
534    #[inline]
535    fn from(error: AttrError) -> Self {
536        Self::InvalidAttr(error)
537    }
538}
539
540impl From<NamespaceError> for Error {
541    #[inline]
542    fn from(error: NamespaceError) -> Self {
543        Self::Namespace(error)
544    }
545}
546
547/// A specialized [`Result`](std::result::Result) with the error hard-wired to [`Error`].
548pub type Result<T> = std::result::Result<T, Error>;
549
550impl fmt::Display for Error {
551    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
552        match self {
553            Self::Io(e) => write!(f, "I/O error: {}", e),
554            Self::Syntax(e) => write!(f, "syntax error: {}", e),
555            Self::IllFormed(e) => write!(f, "ill-formed document: {}", e),
556            Self::NotWellFormed(e) => write!(f, "not well-formed document: {}", e),
557            Self::InvalidAttr(e) => write!(f, "error while parsing attribute: {}", e),
558            Self::Encoding(e) => e.fmt(f),
559            Self::Escape(e) => e.fmt(f),
560            Self::Namespace(e) => e.fmt(f),
561        }
562    }
563}
564
565impl std::error::Error for Error {
566    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
567        match self {
568            Self::Io(e) => Some(e),
569            Self::Syntax(e) => Some(e),
570            Self::IllFormed(e) => Some(e),
571            Self::NotWellFormed(e) => Some(e),
572            Self::InvalidAttr(e) => Some(e),
573            Self::Encoding(e) => Some(e),
574            Self::Escape(e) => Some(e),
575            Self::Namespace(e) => Some(e),
576        }
577    }
578}
579
580#[cfg(feature = "serialize")]
581#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
582pub mod serialize {
583    //! Serde (de)serialization errors.
584    //!
585    //! Mirrors the `quick_xml::errors::serialize` module of quick-xml 0.41.
586
587    use super::*;
588    use crate::utils::write_byte_string;
589    use std::borrow::Cow;
590    #[cfg(feature = "overlapped-lists")]
591    use std::num::NonZeroUsize;
592    use std::str::Utf8Error;
593
594    /// Deserialization error.
595    #[derive(Clone, Debug)]
596    pub enum DeError {
597        /// Serde custom error.
598        Custom(String),
599        /// XML parsing error.
600        InvalidXml(Error),
601        /// This error indicates an error in the [`Deserialize`](serde::Deserialize)
602        /// implementation when reading a map or a struct: `MapAccess::next_value[_seed]`
603        /// was called before `MapAccess::next_key[_seed]`.
604        ///
605        /// You should check your types that implement the corresponding trait.
606        KeyNotRead,
607        /// The deserializer encountered a start tag with a specified name when it
608        /// was not expecting one. This happens when you try to deserialize a
609        /// primitive value (numbers, strings, booleans) from an XML element.
610        UnexpectedStart(Vec<u8>),
611        /// The [`Reader`] produced [`Event::Eof`] when it was not expected, for
612        /// example after producing [`Event::Start`] but before the corresponding
613        /// [`Event::End`].
614        ///
615        /// [`Reader`]: crate::reader::Reader
616        /// [`Event::Eof`]: crate::events::Event::Eof
617        /// [`Event::Start`]: crate::events::Event::Start
618        /// [`Event::End`]: crate::events::Event::End
619        UnexpectedEof,
620        /// Too many events were skipped while deserializing a sequence; the event
621        /// limit was exceeded. The limit is provided as an argument.
622        #[cfg(feature = "overlapped-lists")]
623        #[cfg_attr(docsrs, doc(cfg(feature = "overlapped-lists")))]
624        TooManyEvents(NonZeroUsize),
625    }
626
627    impl fmt::Display for DeError {
628        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
629            match self {
630                Self::Custom(s) => f.write_str(s),
631                Self::InvalidXml(e) => e.fmt(f),
632                Self::KeyNotRead => f.write_str("invalid `Deserialize` implementation: `MapAccess::next_value[_seed]` was called before `MapAccess::next_key[_seed]`"),
633                Self::UnexpectedStart(e) => {
634                    f.write_str("unexpected `Event::Start(")?;
635                    write_byte_string(f, e)?;
636                    f.write_str(")`")
637                }
638                Self::UnexpectedEof => f.write_str("unexpected `Event::Eof`"),
639                #[cfg(feature = "overlapped-lists")]
640                Self::TooManyEvents(s) => write!(f, "deserializer buffered {} events, limit exceeded", s),
641            }
642        }
643    }
644
645    impl std::error::Error for DeError {
646        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
647            match self {
648                Self::InvalidXml(e) => Some(e),
649                _ => None,
650            }
651        }
652    }
653
654    impl serde::de::Error for DeError {
655        fn custom<T: fmt::Display>(msg: T) -> Self {
656            Self::Custom(msg.to_string())
657        }
658    }
659
660    impl From<Error> for DeError {
661        #[inline]
662        fn from(e: Error) -> Self {
663            Self::InvalidXml(e)
664        }
665    }
666
667    impl From<EscapeError> for DeError {
668        #[inline]
669        fn from(e: EscapeError) -> Self {
670            Self::InvalidXml(e.into())
671        }
672    }
673
674    impl From<EncodingError> for DeError {
675        #[inline]
676        fn from(e: EncodingError) -> Self {
677            Self::InvalidXml(e.into())
678        }
679    }
680
681    impl From<AttrError> for DeError {
682        #[inline]
683        fn from(e: AttrError) -> Self {
684            Self::InvalidXml(e.into())
685        }
686    }
687
688    /// Serialization error.
689    #[derive(Clone, Debug)]
690    pub enum SeError {
691        /// Serde custom error.
692        Custom(String),
693        /// An XML document cannot be written to the underlying source.
694        ///
695        /// Contains a reference-counted I/O error to make the error type `Clone`able.
696        Io(Arc<IoError>),
697        /// Some value could not be formatted.
698        Fmt(std::fmt::Error),
699        /// The serialized type cannot be represented in XML due to a violation of
700        /// the XML rules in the final XML document. For example, an attempt to
701        /// serialize a `HashMap<{integer}, ...>` would cause this error because an
702        /// [XML name] cannot start with a digit or a hyphen (minus sign). The same
703        /// result would occur if the map key is a complex type that cannot be
704        /// serialized as a primitive type (i.e. string, char, bool, unit struct or
705        /// unit variant).
706        ///
707        /// [XML name]: https://www.w3.org/TR/xml11/#sec-common-syn
708        Unsupported(Cow<'static, str>),
709        /// Some value could not be turned into UTF-8.
710        NonEncodable(Utf8Error),
711    }
712
713    impl fmt::Display for SeError {
714        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
715            match self {
716                Self::Custom(s) => f.write_str(s),
717                Self::Io(e) => write!(f, "I/O error: {}", e),
718                Self::Fmt(e) => write!(f, "formatting error: {}", e),
719                Self::Unsupported(s) => write!(f, "unsupported value: {}", s),
720                Self::NonEncodable(e) => write!(f, "malformed UTF-8: {}", e),
721            }
722        }
723    }
724
725    impl ::std::error::Error for SeError {
726        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
727            match self {
728                Self::Io(e) => Some(e),
729                _ => None,
730            }
731        }
732    }
733
734    impl serde::ser::Error for SeError {
735        fn custom<T: fmt::Display>(msg: T) -> Self {
736            Self::Custom(msg.to_string())
737        }
738    }
739
740    impl From<IoError> for SeError {
741        #[inline]
742        fn from(e: IoError) -> Self {
743            Self::Io(Arc::new(e))
744        }
745    }
746
747    impl From<Utf8Error> for SeError {
748        #[inline]
749        fn from(e: Utf8Error) -> Self {
750            Self::NonEncodable(e)
751        }
752    }
753
754    impl From<fmt::Error> for SeError {
755        #[inline]
756        fn from(e: fmt::Error) -> Self {
757            Self::Fmt(e)
758        }
759    }
760}
761
762#[cfg(test)]
763mod tests {
764    use super::*;
765
766    #[test]
767    fn display_and_source() {
768        let err: Error = SyntaxError::UnclosedTag.into();
769        assert_eq!(
770            err.to_string(),
771            "syntax error: tag not closed: `>` not found before end of input"
772        );
773        assert!(std::error::Error::source(&err).is_some());
774    }
775
776    #[test]
777    fn io_invalid_data_stays_io() {
778        let io = IoError::new(IoErrorKind::UnexpectedEof, "boom");
779        let err: Error = io.into();
780        assert!(matches!(err, Error::Io(_)));
781    }
782}