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