| 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 | |