Coverage Report

Created: 2025-12-31 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/quick-xml-0.38.4/src/errors.rs
Line
Count
Source
1
//! Error management module
2
3
use crate::encoding::{Decoder, EncodingError};
4
use crate::escape::EscapeError;
5
use crate::events::attributes::AttrError;
6
use crate::name::{NamespaceError, QName};
7
use std::fmt;
8
use std::io::Error as IoError;
9
use 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)]
16
pub 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
37
impl fmt::Display for SyntaxError {
38
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39
0
        match self {
40
0
            Self::InvalidBangMarkup => f.write_str("unknown or missed symbol in markup"),
41
            Self::UnclosedPIOrXmlDecl => {
42
0
                f.write_str("processing instruction or xml declaration not closed: `?>` not found before end of input")
43
            }
44
            Self::UnclosedComment => {
45
0
                f.write_str("comment not closed: `-->` not found before end of input")
46
            }
47
            Self::UnclosedDoctype => {
48
0
                f.write_str("DOCTYPE not closed: `>` not found before end of input")
49
            }
50
            Self::UnclosedCData => {
51
0
                f.write_str("CDATA not closed: `]]>` not found before end of input")
52
            }
53
0
            Self::UnclosedTag => f.write_str("tag not closed: `>` not found before end of input"),
54
        }
55
0
    }
56
}
57
58
impl 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)]
70
pub enum IllFormedError {
71
    /// A `version` attribute was not found in an XML declaration or is not the
72
    /// first attribute.
73
    ///
74
    /// According to the [specification], the XML declaration (`<?xml ?>`) MUST contain
75
    /// a `version` attribute and it MUST be the first attribute. This error indicates,
76
    /// that the declaration does not contain attributes at all (if contains `None`)
77
    /// or either `version` attribute is not present or not the first attribute in
78
    /// the declaration. In the last case it contains the name of the found attribute.
79
    ///
80
    /// [specification]: https://www.w3.org/TR/xml11/#sec-prolog-dtd
81
    MissingDeclVersion(Option<String>),
82
    /// A document type definition (DTD) does not contain a name of a root element.
83
    ///
84
    /// According to the [specification], document type definition (`<!DOCTYPE foo>`)
85
    /// MUST contain a name which defines a document type (`foo`). If that name
86
    /// is missed, this error is returned.
87
    ///
88
    /// [specification]: https://www.w3.org/TR/xml11/#NT-doctypedecl
89
    MissingDoctypeName,
90
    /// The end tag was not found during reading of a sub-tree of elements due to
91
    /// encountering an EOF from the underlying reader. This error is returned from
92
    /// [`Reader::read_to_end`].
93
    ///
94
    /// [`Reader::read_to_end`]: crate::reader::Reader::read_to_end
95
    MissingEndTag(String),
96
    /// The specified end tag was encountered without corresponding open tag at the
97
    /// same level of hierarchy
98
    UnmatchedEndTag(String),
99
    /// The specified end tag does not match the start tag at that nesting level.
100
    MismatchedEndTag {
101
        /// Name of open tag, that is expected to be closed
102
        expected: String,
103
        /// Name of actually closed tag
104
        found: String,
105
    },
106
    /// A comment contains forbidden double-hyphen (`--`) sequence inside.
107
    ///
108
    /// According to the [specification], for compatibility, comments MUST NOT contain
109
    /// double-hyphen (`--`) sequence, in particular, they cannot end by `--->`.
110
    ///
111
    /// The quick-xml by default does not check that, because this restriction is
112
    /// mostly artificial, but you can enable it in the [configuration].
113
    ///
114
    /// [specification]: https://www.w3.org/TR/xml11/#sec-comments
115
    /// [configuration]: crate::reader::Config::check_comments
116
    DoubleHyphenInComment,
117
    /// The parser started to parse entity or character reference (`&...;`) in text,
118
    /// but the input ended before the closing `;` character was found.
119
    UnclosedReference,
120
}
121
122
impl fmt::Display for IllFormedError {
123
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
124
0
        match self {
125
            Self::MissingDeclVersion(None) => {
126
0
                f.write_str("an XML declaration does not contain `version` attribute")
127
            }
128
0
            Self::MissingDeclVersion(Some(attr)) => {
129
0
                write!(f, "an XML declaration must start with `version` attribute, but in starts with `{}`", attr)
130
            }
131
            Self::MissingDoctypeName => {
132
0
                f.write_str("`<!DOCTYPE>` declaration does not contain a name of a document type")
133
            }
134
0
            Self::MissingEndTag(tag) => write!(
135
0
                f,
136
0
                "start tag not closed: `</{}>` not found before end of input",
137
                tag,
138
            ),
139
0
            Self::UnmatchedEndTag(tag) => {
140
0
                write!(f, "close tag `</{}>` does not match any open tag", tag)
141
            }
142
0
            Self::MismatchedEndTag { expected, found } => write!(
143
0
                f,
144
0
                "expected `</{}>`, but `</{}>` was found",
145
                expected, found,
146
            ),
147
            Self::DoubleHyphenInComment => {
148
0
                f.write_str("forbidden string `--` was found in a comment")
149
            }
150
0
            Self::UnclosedReference => f.write_str(
151
0
                "entity or character reference not closed: `;` not found before end of input",
152
            ),
153
        }
154
0
    }
155
}
156
157
impl std::error::Error for IllFormedError {}
158
159
////////////////////////////////////////////////////////////////////////////////////////////////////
160
161
/// The error type used by this crate.
162
#[derive(Clone, Debug)]
163
pub enum Error {
164
    /// XML document cannot be read from underlying source.
165
    ///
166
    /// Contains the reference-counted I/O error to make the error type `Clone`able.
167
    Io(Arc<IoError>),
168
    /// The document does not corresponds to the XML grammar.
169
    Syntax(SyntaxError),
170
    /// The document is not [well-formed](https://www.w3.org/TR/xml11/#dt-wellformed).
171
    IllFormed(IllFormedError),
172
    /// Attribute parsing error
173
    InvalidAttr(AttrError),
174
    /// Encoding error
175
    Encoding(EncodingError),
176
    /// Escape error
177
    Escape(EscapeError),
178
    /// Parsed XML has some namespace-related problems
179
    Namespace(NamespaceError),
180
}
181
182
impl Error {
183
0
    pub(crate) fn missed_end(name: QName, decoder: Decoder) -> Self {
184
0
        match decoder.decode(name.as_ref()) {
185
0
            Ok(name) => IllFormedError::MissingEndTag(name.into()).into(),
186
0
            Err(err) => err.into(),
187
        }
188
0
    }
189
}
190
191
impl From<IoError> for Error {
192
    /// Creates a new `Error::Io` from the given error
193
    #[inline]
194
0
    fn from(error: IoError) -> Error {
195
0
        Self::Io(Arc::new(error))
196
0
    }
197
}
198
199
impl From<SyntaxError> for Error {
200
    /// Creates a new `Error::Syntax` from the given error
201
    #[inline]
202
0
    fn from(error: SyntaxError) -> Self {
203
0
        Self::Syntax(error)
204
0
    }
205
}
206
207
impl From<IllFormedError> for Error {
208
    /// Creates a new `Error::IllFormed` from the given error
209
    #[inline]
210
0
    fn from(error: IllFormedError) -> Self {
211
0
        Self::IllFormed(error)
212
0
    }
213
}
214
215
impl From<EncodingError> for Error {
216
    /// Creates a new `Error::EncodingError` from the given error
217
    #[inline]
218
0
    fn from(error: EncodingError) -> Error {
219
0
        Self::Encoding(error)
220
0
    }
221
}
222
223
impl From<EscapeError> for Error {
224
    /// Creates a new `Error::EscapeError` from the given error
225
    #[inline]
226
0
    fn from(error: EscapeError) -> Error {
227
0
        Self::Escape(error)
228
0
    }
229
}
230
231
impl From<AttrError> for Error {
232
    #[inline]
233
0
    fn from(error: AttrError) -> Self {
234
0
        Self::InvalidAttr(error)
235
0
    }
236
}
237
238
impl From<NamespaceError> for Error {
239
    #[inline]
240
0
    fn from(error: NamespaceError) -> Self {
241
0
        Self::Namespace(error)
242
0
    }
243
}
244
245
/// A specialized `Result` type where the error is hard-wired to [`Error`].
246
pub type Result<T> = std::result::Result<T, Error>;
247
248
impl fmt::Display for Error {
249
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
250
0
        match self {
251
0
            Self::Io(e) => write!(f, "I/O error: {}", e),
252
0
            Self::Syntax(e) => write!(f, "syntax error: {}", e),
253
0
            Self::IllFormed(e) => write!(f, "ill-formed document: {}", e),
254
0
            Self::InvalidAttr(e) => write!(f, "error while parsing attribute: {}", e),
255
0
            Self::Encoding(e) => e.fmt(f),
256
0
            Self::Escape(e) => e.fmt(f),
257
0
            Self::Namespace(e) => e.fmt(f),
258
        }
259
0
    }
260
}
261
262
impl std::error::Error for Error {
263
0
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
264
0
        match self {
265
0
            Self::Io(e) => Some(e),
266
0
            Self::Syntax(e) => Some(e),
267
0
            Self::IllFormed(e) => Some(e),
268
0
            Self::InvalidAttr(e) => Some(e),
269
0
            Self::Encoding(e) => Some(e),
270
0
            Self::Escape(e) => Some(e),
271
0
            Self::Namespace(e) => Some(e),
272
        }
273
0
    }
274
}
275
276
#[cfg(feature = "serialize")]
277
pub mod serialize {
278
    //! A module to handle serde (de)serialization errors
279
280
    use super::*;
281
    use crate::utils::write_byte_string;
282
    use std::borrow::Cow;
283
    #[cfg(feature = "overlapped-lists")]
284
    use std::num::NonZeroUsize;
285
    use std::str::Utf8Error;
286
287
    /// (De)serialization error
288
    #[derive(Clone, Debug)]
289
    pub enum DeError {
290
        /// Serde custom error
291
        Custom(String),
292
        /// Xml parsing error
293
        InvalidXml(Error),
294
        /// This error indicates an error in the [`Deserialize`](serde::Deserialize)
295
        /// implementation when read a map or a struct: `MapAccess::next_value[_seed]`
296
        /// was called before `MapAccess::next_key[_seed]`.
297
        ///
298
        /// You should check your types, that implements corresponding trait.
299
        KeyNotRead,
300
        /// Deserializer encounter a start tag with a specified name when it is
301
        /// not expecting. This happens when you try to deserialize a primitive
302
        /// value (numbers, strings, booleans) from an XML element.
303
        UnexpectedStart(Vec<u8>),
304
        /// The [`Reader`] produced [`Event::Eof`] when it is not expecting,
305
        /// for example, after producing [`Event::Start`] but before corresponding
306
        /// [`Event::End`].
307
        ///
308
        /// [`Reader`]: crate::reader::Reader
309
        /// [`Event::Eof`]: crate::events::Event::Eof
310
        /// [`Event::Start`]: crate::events::Event::Start
311
        /// [`Event::End`]: crate::events::Event::End
312
        UnexpectedEof,
313
        /// Too many events were skipped while deserializing a sequence, event limit
314
        /// exceeded. The limit was provided as an argument
315
        #[cfg(feature = "overlapped-lists")]
316
        TooManyEvents(NonZeroUsize),
317
    }
318
319
    impl fmt::Display for DeError {
320
0
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
321
0
            match self {
322
0
                Self::Custom(s) => f.write_str(s),
323
0
                Self::InvalidXml(e) => e.fmt(f),
324
0
                Self::KeyNotRead => f.write_str("invalid `Deserialize` implementation: `MapAccess::next_value[_seed]` was called before `MapAccess::next_key[_seed]`"),
325
0
                Self::UnexpectedStart(e) => {
326
0
                    f.write_str("unexpected `Event::Start(")?;
327
0
                    write_byte_string(f, e)?;
328
0
                    f.write_str(")`")
329
                }
330
0
                Self::UnexpectedEof => f.write_str("unexpected `Event::Eof`"),
331
                #[cfg(feature = "overlapped-lists")]
332
0
                Self::TooManyEvents(s) => write!(f, "deserializer buffered {} events, limit exceeded", s),
333
            }
334
0
        }
335
    }
336
337
    impl std::error::Error for DeError {
338
0
        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
339
0
            match self {
340
0
                Self::InvalidXml(e) => Some(e),
341
0
                _ => None,
342
            }
343
0
        }
344
    }
345
346
    impl serde::de::Error for DeError {
347
0
        fn custom<T: fmt::Display>(msg: T) -> Self {
348
0
            Self::Custom(msg.to_string())
349
0
        }
350
    }
351
352
    impl From<Error> for DeError {
353
        #[inline]
354
0
        fn from(e: Error) -> Self {
355
0
            Self::InvalidXml(e)
356
0
        }
357
    }
358
359
    impl From<EscapeError> for DeError {
360
        #[inline]
361
0
        fn from(e: EscapeError) -> Self {
362
0
            Self::InvalidXml(e.into())
363
0
        }
364
    }
365
366
    impl From<EncodingError> for DeError {
367
        #[inline]
368
0
        fn from(e: EncodingError) -> Self {
369
0
            Self::InvalidXml(e.into())
370
0
        }
371
    }
372
373
    impl From<AttrError> for DeError {
374
        #[inline]
375
0
        fn from(e: AttrError) -> Self {
376
0
            Self::InvalidXml(e.into())
377
0
        }
378
    }
379
380
    /// Serialization error
381
    #[derive(Clone, Debug)]
382
    pub enum SeError {
383
        /// Serde custom error
384
        Custom(String),
385
        /// XML document cannot be written to underlying source.
386
        ///
387
        /// Contains the reference-counted I/O error to make the error type `Clone`able.
388
        Io(Arc<IoError>),
389
        /// Some value could not be formatted
390
        Fmt(std::fmt::Error),
391
        /// Serialized type cannot be represented in an XML due to violation of the
392
        /// XML rules in the final XML document. For example, attempt to serialize
393
        /// a `HashMap<{integer}, ...>` would cause this error because [XML name]
394
        /// cannot start from a digit or a hyphen (minus sign). The same result
395
        /// would occur if map key is a complex type that cannot be serialized as
396
        /// a primitive type (i.e. string, char, bool, unit struct or unit variant).
397
        ///
398
        /// [XML name]: https://www.w3.org/TR/xml11/#sec-common-syn
399
        Unsupported(Cow<'static, str>),
400
        /// Some value could not be turned to UTF-8
401
        NonEncodable(Utf8Error),
402
    }
403
404
    impl fmt::Display for SeError {
405
0
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
406
0
            match self {
407
0
                Self::Custom(s) => f.write_str(s),
408
0
                Self::Io(e) => write!(f, "I/O error: {}", e),
409
0
                Self::Fmt(e) => write!(f, "formatting error: {}", e),
410
0
                Self::Unsupported(s) => write!(f, "unsupported value: {}", s),
411
0
                Self::NonEncodable(e) => write!(f, "malformed UTF-8: {}", e),
412
            }
413
0
        }
414
    }
415
416
    impl ::std::error::Error for SeError {
417
0
        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
418
0
            match self {
419
0
                Self::Io(e) => Some(e),
420
0
                _ => None,
421
            }
422
0
        }
423
    }
424
425
    impl serde::ser::Error for SeError {
426
0
        fn custom<T: fmt::Display>(msg: T) -> Self {
427
0
            Self::Custom(msg.to_string())
428
0
        }
429
    }
430
431
    impl From<IoError> for SeError {
432
        #[inline]
433
0
        fn from(e: IoError) -> Self {
434
0
            Self::Io(Arc::new(e))
435
0
        }
436
    }
437
438
    impl From<Utf8Error> for SeError {
439
        #[inline]
440
0
        fn from(e: Utf8Error) -> Self {
441
0
            Self::NonEncodable(e)
442
0
        }
443
    }
444
445
    impl From<fmt::Error> for SeError {
446
        #[inline]
447
0
        fn from(e: fmt::Error) -> Self {
448
0
            Self::Fmt(e)
449
0
        }
450
    }
451
}