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