diff --git a/Changelog.md b/Changelog.md index f43f525e..d05a1ed8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -22,9 +22,11 @@ - [#227]: Split `SeError` from `DeError` in the `serialize` feature. Serialize functions and methods now return `SeError`. - [#810]: Return `std::io::Error` from `Writer` methods. +- [#811]: Split `NamespaceError` from `Error`. [#227]: https://github.com/tafia/quick-xml/issues/227 [#810]: https://github.com/tafia/quick-xml/pull/810 +[#811]: https://github.com/tafia/quick-xml/pull/811 ## 0.36.2 -- 2024-09-20 diff --git a/src/errors.rs b/src/errors.rs index 669ea392..4765234d 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -3,8 +3,7 @@ use crate::encoding::Decoder; use crate::escape::EscapeError; use crate::events::attributes::AttrError; -use crate::name::QName; -use crate::utils::write_byte_string; +use crate::name::{NamespaceError, QName}; use std::fmt; use std::io::Error as IoError; use std::str::Utf8Error; @@ -175,24 +174,8 @@ pub enum Error { InvalidAttr(AttrError), /// Escape error EscapeError(EscapeError), - /// Specified namespace prefix is unknown, cannot resolve namespace for it - UnknownPrefix(Vec), - /// Error for when a reserved namespace is set incorrectly. - /// - /// This error returned in following cases: - /// - the XML document attempts to bind `xml` prefix to something other than - /// `http://www.w3.org/XML/1998/namespace` - /// - the XML document attempts to bind `xmlns` prefix - /// - the XML document attempts to bind some prefix (except `xml`) to - /// `http://www.w3.org/XML/1998/namespace` - /// - the XML document attempts to bind some prefix to - /// `http://www.w3.org/2000/xmlns/` - InvalidPrefixBind { - /// The prefix that is tried to be bound - prefix: Vec, - /// Namespace to which prefix tried to be bound - namespace: Vec, - }, + /// Parsed XML has some namespace-related problems + Namespace(NamespaceError), } impl Error { @@ -259,6 +242,13 @@ impl From for Error { } } +impl From for Error { + #[inline] + fn from(error: NamespaceError) -> Self { + Self::Namespace(error) + } +} + /// A specialized `Result` type where the error is hard-wired to [`Error`]. pub type Result = std::result::Result; @@ -272,18 +262,7 @@ impl fmt::Display for Error { Self::NonDecodable(Some(e)) => write!(f, "Malformed UTF-8 input: {}", e), Self::InvalidAttr(e) => write!(f, "error while parsing attribute: {}", e), Self::EscapeError(e) => e.fmt(f), - Self::UnknownPrefix(prefix) => { - f.write_str("Unknown namespace prefix '")?; - write_byte_string(f, prefix)?; - f.write_str("'") - } - Self::InvalidPrefixBind { prefix, namespace } => { - f.write_str("The namespace prefix '")?; - write_byte_string(f, prefix)?; - f.write_str("' cannot be bound to '")?; - write_byte_string(f, namespace)?; - f.write_str("'") - } + Self::Namespace(e) => e.fmt(f), } } } @@ -297,6 +276,7 @@ impl std::error::Error for Error { Self::NonDecodable(Some(e)) => Some(e), Self::InvalidAttr(e) => Some(e), Self::EscapeError(e) => Some(e), + Self::Namespace(e) => Some(e), _ => None, } } @@ -307,6 +287,7 @@ pub mod serialize { //! A module to handle serde (de)serialization errors use super::*; + use crate::utils::write_byte_string; use std::borrow::Cow; #[cfg(feature = "overlapped-lists")] use std::num::NonZeroUsize; diff --git a/src/name.rs b/src/name.rs index 719436c1..6aad4e60 100644 --- a/src/name.rs +++ b/src/name.rs @@ -3,13 +3,58 @@ //! //! [spec]: https://www.w3.org/TR/xml-names11 -use crate::errors::{Error, Result}; use crate::events::attributes::Attribute; use crate::events::BytesStart; use crate::utils::write_byte_string; use memchr::memchr; use std::fmt::{self, Debug, Formatter}; +/// Some namespace was invalid +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum NamespaceError { + /// Specified namespace prefix is unknown, cannot resolve namespace for it + UnknownPrefix(Vec), + /// Error for when a reserved namespace is set incorrectly. + /// + /// This error returned in following cases: + /// - the XML document attempts to bind `xml` prefix to something other than + /// `http://www.w3.org/XML/1998/namespace` + /// - the XML document attempts to bind `xmlns` prefix + /// - the XML document attempts to bind some prefix (except `xml`) to + /// `http://www.w3.org/XML/1998/namespace` + /// - the XML document attempts to bind some prefix to + /// `http://www.w3.org/2000/xmlns/` + InvalidPrefixBind { + /// The prefix that is tried to be bound + prefix: Vec, + /// Namespace to which prefix tried to be bound + namespace: Vec, + }, +} + +impl fmt::Display for NamespaceError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::UnknownPrefix(prefix) => { + f.write_str("unknown namespace prefix '")?; + write_byte_string(f, prefix)?; + f.write_str("'") + } + Self::InvalidPrefixBind { prefix, namespace } => { + f.write_str("the namespace prefix '")?; + write_byte_string(f, prefix)?; + f.write_str("' cannot be bound to '")?; + write_byte_string(f, namespace)?; + f.write_str("'") + } + } + } +} + +impl std::error::Error for NamespaceError {} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + /// A [qualified name] of an element or an attribute, including an optional /// namespace [prefix](Prefix) and a [local name](LocalName). /// @@ -307,17 +352,17 @@ impl<'ns> Debug for ResolveResult<'ns> { } impl<'ns> TryFrom> for Option> { - type Error = Error; + type Error = NamespaceError; /// Try to convert this result to an optional namespace and returns - /// [`Error::UnknownPrefix`] if this result represents unknown prefix - fn try_from(result: ResolveResult<'ns>) -> Result { + /// [`NamespaceError::UnknownPrefix`] if this result represents unknown prefix + fn try_from(result: ResolveResult<'ns>) -> Result { use ResolveResult::*; match result { Unbound => Ok(None), Bound(ns) => Ok(Some(ns)), - Unknown(p) => Err(Error::UnknownPrefix(p)), + Unknown(p) => Err(NamespaceError::UnknownPrefix(p)), } } } @@ -456,7 +501,7 @@ impl NamespaceResolver { /// the specified start element. /// /// [namespace binding]: https://www.w3.org/TR/xml-names11/#dt-NSDecl - pub fn push(&mut self, start: &BytesStart) -> Result<()> { + pub fn push(&mut self, start: &BytesStart) -> Result<(), NamespaceError> { self.nesting_level += 1; let level = self.nesting_level; // adds new namespaces for attributes starting with 'xmlns:' and for the 'xmlns' @@ -477,7 +522,7 @@ impl NamespaceResolver { Some(PrefixDeclaration::Named(b"xml")) => { if Namespace(&v) != RESERVED_NAMESPACE_XML.1 { // error, `xml` prefix explicitly set to different value - return Err(Error::InvalidPrefixBind { + return Err(NamespaceError::InvalidPrefixBind { prefix: b"xml".to_vec(), namespace: v.to_vec(), }); @@ -486,7 +531,7 @@ impl NamespaceResolver { } Some(PrefixDeclaration::Named(b"xmlns")) => { // error, `xmlns` prefix explicitly set - return Err(Error::InvalidPrefixBind { + return Err(NamespaceError::InvalidPrefixBind { prefix: b"xmlns".to_vec(), namespace: v.to_vec(), }); @@ -497,7 +542,7 @@ impl NamespaceResolver { if ns == RESERVED_NAMESPACE_XML.1 || ns == RESERVED_NAMESPACE_XMLNS.1 { // error, non-`xml` prefix set to xml uri // error, non-`xmlns` prefix set to xmlns uri - return Err(Error::InvalidPrefixBind { + return Err(NamespaceError::InvalidPrefixBind { prefix: prefix.to_vec(), namespace: v.to_vec(), }); @@ -984,7 +1029,7 @@ mod namespaces { " xmlns:xml='not_correct_namespace'", 0, )) { - Err(Error::InvalidPrefixBind { prefix, namespace }) => { + Err(NamespaceError::InvalidPrefixBind { prefix, namespace }) => { assert_eq!(prefix, b"xml"); assert_eq!(namespace, b"not_correct_namespace"); } @@ -1002,7 +1047,7 @@ mod namespaces { let mut resolver = NamespaceResolver::default(); let s = resolver.buffer.len(); match resolver.push(&BytesStart::from_content(" xmlns:xml=''", 0)) { - Err(Error::InvalidPrefixBind { prefix, namespace }) => { + Err(NamespaceError::InvalidPrefixBind { prefix, namespace }) => { assert_eq!(prefix, b"xml"); assert_eq!(namespace, b""); } @@ -1023,7 +1068,7 @@ mod namespaces { " xmlns:not_xml='http://www.w3.org/XML/1998/namespace'", 0, )) { - Err(Error::InvalidPrefixBind { prefix, namespace }) => { + Err(NamespaceError::InvalidPrefixBind { prefix, namespace }) => { assert_eq!(prefix, b"not_xml"); assert_eq!(namespace, b"http://www.w3.org/XML/1998/namespace"); } @@ -1069,7 +1114,7 @@ mod namespaces { " xmlns:xmlns='http://www.w3.org/2000/xmlns/'", 0, )) { - Err(Error::InvalidPrefixBind { prefix, namespace }) => { + Err(NamespaceError::InvalidPrefixBind { prefix, namespace }) => { assert_eq!(prefix, b"xmlns"); assert_eq!(namespace, b"http://www.w3.org/2000/xmlns/"); } @@ -1090,7 +1135,7 @@ mod namespaces { " xmlns:xmlns='not_correct_namespace'", 0, )) { - Err(Error::InvalidPrefixBind { prefix, namespace }) => { + Err(NamespaceError::InvalidPrefixBind { prefix, namespace }) => { assert_eq!(prefix, b"xmlns"); assert_eq!(namespace, b"not_correct_namespace"); } @@ -1108,7 +1153,7 @@ mod namespaces { let mut resolver = NamespaceResolver::default(); let s = resolver.buffer.len(); match resolver.push(&BytesStart::from_content(" xmlns:xmlns=''", 0)) { - Err(Error::InvalidPrefixBind { prefix, namespace }) => { + Err(NamespaceError::InvalidPrefixBind { prefix, namespace }) => { assert_eq!(prefix, b"xmlns"); assert_eq!(namespace, b""); } @@ -1129,7 +1174,7 @@ mod namespaces { " xmlns:not_xmlns='http://www.w3.org/2000/xmlns/'", 0, )) { - Err(Error::InvalidPrefixBind { prefix, namespace }) => { + Err(NamespaceError::InvalidPrefixBind { prefix, namespace }) => { assert_eq!(prefix, b"not_xmlns"); assert_eq!(namespace, b"http://www.w3.org/2000/xmlns/"); }