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}