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