use alloc::string::String;
use core::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Error {
pub kind: ErrorKind,
pub offset: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ErrorKind {
UnexpectedEof,
MismatchedEndTag {
expected: String,
found: String,
},
UnexpectedEndTag(String),
InvalidName,
UnquotedAttributeValue,
DuplicateAttribute(String),
UnknownEntity(String),
UnboundPrefix(String),
TrailingContent,
NoRootElement,
Unterminated(&'static str),
}
impl Error {
pub(crate) const fn new(kind: ErrorKind, offset: usize) -> Self {
Self { kind, offset }
}
#[must_use]
pub fn line_column(&self, input: &str) -> (usize, usize) {
let upto = &input[..self.offset.min(input.len())];
let line = upto.matches('\n').count() + 1;
let column = upto
.rsplit('\n')
.next()
.map_or(1, |l| l.chars().count() + 1);
(line, column)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "at byte {}: ", self.offset)?;
match &self.kind {
ErrorKind::UnexpectedEof => f.write_str("input ended unexpectedly"),
ErrorKind::MismatchedEndTag { expected, found } => {
write!(f, "</{found}> closes <{expected}>")
}
ErrorKind::UnexpectedEndTag(n) => {
write!(f, "</{n}> has no matching open tag")
}
ErrorKind::InvalidName => f.write_str("expected a name"),
ErrorKind::UnquotedAttributeValue => {
f.write_str("attribute value must be quoted")
}
ErrorKind::DuplicateAttribute(n) => {
write!(f, "duplicate attribute {n}")
}
ErrorKind::UnknownEntity(n) => {
write!(f, "unknown entity &{n};")
}
ErrorKind::UnboundPrefix(p) => {
write!(f, "namespace prefix {p} is not declared")
}
ErrorKind::TrailingContent => {
f.write_str("content after the root element")
}
ErrorKind::NoRootElement => {
f.write_str("document has no root element")
}
ErrorKind::Unterminated(what) => {
write!(f, "unterminated {what}")
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
pub type Result<T> = core::result::Result<T, Error>;