use crate::encoding::EncodingError;
use crate::escape::EscapeError;
use crate::events::attributes::AttrError;
use crate::name::NamespaceError;
use std::fmt;
use std::io::{Error as IoError, ErrorKind as IoErrorKind};
use std::sync::Arc;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum SyntaxError {
InvalidBangMarkup,
UnclosedPI,
UnclosedXmlDecl,
UnclosedComment,
UnclosedDoctype,
UnclosedCData,
UnclosedTag,
UnclosedSingleQuotedAttributeValue,
UnclosedDoubleQuotedAttributeValue,
}
impl fmt::Display for SyntaxError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::InvalidBangMarkup => f.write_str("unknown or missed symbol in markup"),
Self::UnclosedPI => {
f.write_str("processing instruction not closed: `?>` not found before end of input")
}
Self::UnclosedXmlDecl => {
f.write_str("XML declaration not closed: `?>` not found before end of input")
}
Self::UnclosedComment => {
f.write_str("comment not closed: `-->` not found before end of input")
}
Self::UnclosedDoctype => {
f.write_str("DOCTYPE not closed: `>` not found before end of input")
}
Self::UnclosedCData => {
f.write_str("CDATA not closed: `]]>` not found before end of input")
}
Self::UnclosedTag => f.write_str("tag not closed: `>` not found before end of input"),
Self::UnclosedSingleQuotedAttributeValue => {
f.write_str("attribute value not closed: `'` not found before end of input")
}
Self::UnclosedDoubleQuotedAttributeValue => {
f.write_str("attribute value not closed: `\"` not found before end of input")
}
}
}
}
impl std::error::Error for SyntaxError {}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum IllFormedError {
MissingDeclVersion(Option<String>),
UnknownVersion,
MissingDoctypeName,
MissingEndTag(String),
UnmatchedEndTag(String),
MismatchedEndTag {
expected: String,
found: String,
},
DoubleHyphenInComment,
UnclosedReference,
}
impl fmt::Display for IllFormedError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::MissingDeclVersion(None) => {
f.write_str("an XML declaration does not contain `version` attribute")
}
Self::MissingDeclVersion(Some(attr)) => {
write!(
f,
"an XML declaration must start with `version` attribute, but in starts with `{}`",
attr
)
}
Self::UnknownVersion => {
f.write_str("unknown XML version: either 1.0 or 1.1 is expected")
}
Self::MissingDoctypeName => {
f.write_str("`<!DOCTYPE>` declaration does not contain a name of a document type")
}
Self::MissingEndTag(tag) => write!(
f,
"start tag not closed: `</{}>` not found before end of input",
tag,
),
Self::UnmatchedEndTag(tag) => {
write!(f, "close tag `</{}>` does not match any open tag", tag)
}
Self::MismatchedEndTag { expected, found } => {
write!(
f,
"expected `</{}>`, but `</{}>` was found",
expected, found
)
}
Self::DoubleHyphenInComment => {
f.write_str("forbidden string `--` was found in a comment")
}
Self::UnclosedReference => f.write_str(
"entity or character reference not closed: `;` not found before end of input",
),
}
}
}
impl std::error::Error for IllFormedError {}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum WellFormedError {
InvalidCharacter(char),
InvalidName(String),
InvalidPublicId(String),
InvalidReference(String),
UndeclaredEntity(String),
RecursiveEntity(String),
EntityExpansionLimit {
entity: String,
limit: &'static str,
},
UnparsedEntityReference(String),
IllFormedEntity {
entity: String,
reason: String,
},
LessThanInAttributeValue(String),
ExternalEntityInAttributeValue(String),
DuplicateAttribute(String),
InvalidQName(String),
EmptyPrefixBinding(String),
MalformedTag(String),
CDataEndInContent,
ReservedPiTarget(String),
MisplacedXmlDeclaration,
InvalidXmlDeclaration(String),
MisplacedDoctype,
MissingRootElement,
MultipleRootElements,
ContentOutsideRoot,
InvalidDoctype(String),
}
impl fmt::Display for WellFormedError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::InvalidCharacter(ch) => {
write!(
f,
"character U+{:04X} is not allowed by the `Char` production",
*ch as u32
)
}
Self::InvalidName(name) => {
write!(f, "`{}` does not match the `Name` production", name)
}
Self::InvalidPublicId(id) => write!(
f,
"public identifier `{}` contains a character outside `PubidChar`",
id
),
Self::InvalidReference(reference) => {
write!(
f,
"`{}` is not a valid entity or character reference",
reference
)
}
Self::UndeclaredEntity(name) => {
write!(f, "reference to the undeclared entity `{}`", name)
}
Self::RecursiveEntity(name) => {
write!(f, "entity `{}` refers to itself", name)
}
Self::EntityExpansionLimit { entity, limit } => write!(
f,
"expanding the entity `{}` exceeds the {} of this processor",
entity, limit
),
Self::UnparsedEntityReference(name) => write!(
f,
"reference to the unparsed (NDATA) entity `{}` is not allowed here",
name
),
Self::IllFormedEntity { entity, reason } => write!(
f,
"replacement text of the entity `{}` is not well-formed: {}",
entity, reason
),
Self::LessThanInAttributeValue(name) => write!(
f,
"value of the attribute `{}` contains a literal `<`",
name
),
Self::ExternalEntityInAttributeValue(name) => write!(
f,
"an attribute value cannot refer to the external entity `{}`",
name
),
Self::DuplicateAttribute(name) => {
write!(f, "the attribute `{}` is specified more than once", name)
}
Self::InvalidQName(name) => {
write!(f, "`{}` does not match the `QName` production", name)
}
Self::EmptyPrefixBinding(prefix) => write!(
f,
"the prefix `{}` cannot be bound to the empty namespace name",
prefix
),
Self::MalformedTag(reason) => write!(f, "malformed tag: {}", reason),
Self::CDataEndInContent => {
f.write_str("the sequence `]]>` is not allowed in character data")
}
Self::ReservedPiTarget(target) => write!(
f,
"`{}` is a reserved processing instruction target",
target
),
Self::MisplacedXmlDeclaration => f.write_str(
"the XML declaration must be at the very beginning of the document entity",
),
Self::InvalidXmlDeclaration(reason) => {
write!(f, "malformed XML declaration: {}", reason)
}
Self::MisplacedDoctype => {
f.write_str("`<!DOCTYPE>` must appear once, before the root element")
}
Self::MissingRootElement => f.write_str("the document has no root element"),
Self::MultipleRootElements => {
f.write_str("the document has more than one root element")
}
Self::ContentOutsideRoot => {
f.write_str("character data is not allowed outside of the root element")
}
Self::InvalidDoctype(reason) => {
write!(f, "malformed document type declaration: {}", reason)
}
}
}
}
impl std::error::Error for WellFormedError {}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum Error {
Io(Arc<IoError>),
Syntax(SyntaxError),
IllFormed(IllFormedError),
NotWellFormed(WellFormedError),
InvalidAttr(AttrError),
Encoding(EncodingError),
Escape(EscapeError),
Namespace(NamespaceError),
}
impl Error {
pub(crate) fn missed_end(name: crate::name::QName, decoder: crate::encoding::Decoder) -> Self {
match decoder.decode(name.as_ref()) {
Ok(name) => IllFormedError::MissingEndTag(name.into_owned()).into(),
Err(err) => err.into(),
}
}
#[cfg(feature = "serialize")]
pub(crate) fn unmatched_end(
name: crate::name::QName,
decoder: crate::encoding::Decoder,
) -> Self {
match decoder.decode(name.as_ref()) {
Ok(name) => IllFormedError::UnmatchedEndTag(name.into_owned()).into(),
Err(err) => err.into(),
}
}
}
impl From<IoError> for Error {
#[inline]
fn from(error: IoError) -> Error {
match error.kind() {
IoErrorKind::InvalidData => match error.downcast::<EncodingError>() {
Ok(err) => Self::Encoding(err),
Err(err) => Self::Io(Arc::new(err)),
},
_ => Self::Io(Arc::new(error)),
}
}
}
impl From<SyntaxError> for Error {
#[inline]
fn from(error: SyntaxError) -> Self {
Self::Syntax(error)
}
}
impl From<WellFormedError> for Error {
#[inline]
fn from(error: WellFormedError) -> Self {
Self::NotWellFormed(error)
}
}
impl From<IllFormedError> for Error {
#[inline]
fn from(error: IllFormedError) -> Self {
Self::IllFormed(error)
}
}
impl From<EncodingError> for Error {
#[inline]
fn from(error: EncodingError) -> Error {
Self::Encoding(error)
}
}
impl From<EscapeError> for Error {
#[inline]
fn from(error: EscapeError) -> Error {
Self::Escape(error)
}
}
impl From<AttrError> for Error {
#[inline]
fn from(error: AttrError) -> Self {
Self::InvalidAttr(error)
}
}
impl From<NamespaceError> for Error {
#[inline]
fn from(error: NamespaceError) -> Self {
Self::Namespace(error)
}
}
pub type Result<T> = std::result::Result<T, Error>;
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Io(e) => write!(f, "I/O error: {}", e),
Self::Syntax(e) => write!(f, "syntax error: {}", e),
Self::IllFormed(e) => write!(f, "ill-formed document: {}", e),
Self::NotWellFormed(e) => write!(f, "not well-formed document: {}", e),
Self::InvalidAttr(e) => write!(f, "error while parsing attribute: {}", e),
Self::Encoding(e) => e.fmt(f),
Self::Escape(e) => e.fmt(f),
Self::Namespace(e) => e.fmt(f),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(e) => Some(e),
Self::Syntax(e) => Some(e),
Self::IllFormed(e) => Some(e),
Self::NotWellFormed(e) => Some(e),
Self::InvalidAttr(e) => Some(e),
Self::Encoding(e) => Some(e),
Self::Escape(e) => Some(e),
Self::Namespace(e) => Some(e),
}
}
}
#[cfg(feature = "serialize")]
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub mod serialize {
use super::*;
use crate::utils::write_byte_string;
use std::borrow::Cow;
#[cfg(feature = "overlapped-lists")]
use std::num::NonZeroUsize;
use std::str::Utf8Error;
#[derive(Clone, Debug)]
pub enum DeError {
Custom(String),
InvalidXml(Error),
KeyNotRead,
UnexpectedStart(Vec<u8>),
UnexpectedEof,
#[cfg(feature = "overlapped-lists")]
#[cfg_attr(docsrs, doc(cfg(feature = "overlapped-lists")))]
TooManyEvents(NonZeroUsize),
}
impl fmt::Display for DeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Custom(s) => f.write_str(s),
Self::InvalidXml(e) => e.fmt(f),
Self::KeyNotRead => f.write_str("invalid `Deserialize` implementation: `MapAccess::next_value[_seed]` was called before `MapAccess::next_key[_seed]`"),
Self::UnexpectedStart(e) => {
f.write_str("unexpected `Event::Start(")?;
write_byte_string(f, e)?;
f.write_str(")`")
}
Self::UnexpectedEof => f.write_str("unexpected `Event::Eof`"),
#[cfg(feature = "overlapped-lists")]
Self::TooManyEvents(s) => write!(f, "deserializer buffered {} events, limit exceeded", s),
}
}
}
impl std::error::Error for DeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::InvalidXml(e) => Some(e),
_ => None,
}
}
}
impl serde::de::Error for DeError {
fn custom<T: fmt::Display>(msg: T) -> Self {
Self::Custom(msg.to_string())
}
}
impl From<Error> for DeError {
#[inline]
fn from(e: Error) -> Self {
Self::InvalidXml(e)
}
}
impl From<EscapeError> for DeError {
#[inline]
fn from(e: EscapeError) -> Self {
Self::InvalidXml(e.into())
}
}
impl From<EncodingError> for DeError {
#[inline]
fn from(e: EncodingError) -> Self {
Self::InvalidXml(e.into())
}
}
impl From<AttrError> for DeError {
#[inline]
fn from(e: AttrError) -> Self {
Self::InvalidXml(e.into())
}
}
#[derive(Clone, Debug)]
pub enum SeError {
Custom(String),
Io(Arc<IoError>),
Fmt(std::fmt::Error),
Unsupported(Cow<'static, str>),
NonEncodable(Utf8Error),
}
impl fmt::Display for SeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Custom(s) => f.write_str(s),
Self::Io(e) => write!(f, "I/O error: {}", e),
Self::Fmt(e) => write!(f, "formatting error: {}", e),
Self::Unsupported(s) => write!(f, "unsupported value: {}", s),
Self::NonEncodable(e) => write!(f, "malformed UTF-8: {}", e),
}
}
}
impl ::std::error::Error for SeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(e) => Some(e),
_ => None,
}
}
}
impl serde::ser::Error for SeError {
fn custom<T: fmt::Display>(msg: T) -> Self {
Self::Custom(msg.to_string())
}
}
impl From<IoError> for SeError {
#[inline]
fn from(e: IoError) -> Self {
Self::Io(Arc::new(e))
}
}
impl From<Utf8Error> for SeError {
#[inline]
fn from(e: Utf8Error) -> Self {
Self::NonEncodable(e)
}
}
impl From<fmt::Error> for SeError {
#[inline]
fn from(e: fmt::Error) -> Self {
Self::Fmt(e)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display_and_source() {
let err: Error = SyntaxError::UnclosedTag.into();
assert_eq!(
err.to_string(),
"syntax error: tag not closed: `>` not found before end of input"
);
assert!(std::error::Error::source(&err).is_some());
}
#[test]
fn io_invalid_data_stays_io() {
let io = IoError::new(IoErrorKind::UnexpectedEof, "boom");
let err: Error = io.into();
assert!(matches!(err, Error::Io(_)));
}
}