use std::borrow::Cow;
use std::str::Utf8Error;
use quick_xml::events::attributes::{
AttrError,
Attribute as QuickXMLAttribute,
Attributes,
};
use quick_xml::name::QName;
use quick_xml::Error as QuickXMLError;
use crate::escape::EscapeError;
#[derive(Debug, Clone, PartialEq)]
pub struct Attribute<'data>
{
inner: QuickXMLAttribute<'data>,
}
impl<'data> Attribute<'data>
{
pub fn new(
key: &'data (impl AsRef<[u8]> + ?Sized),
value: impl Into<Cow<'data, [u8]>>,
) -> Self
{
Self {
inner: QuickXMLAttribute {
key: QName(key.as_ref()),
value: value.into(),
},
}
}
pub fn key(&self) -> Result<&str, Error>
{
std::str::from_utf8(self.key_bytes()).map_err(Error::KeyNotUTF8)
}
#[must_use]
pub fn key_bytes(&self) -> &[u8]
{
self.inner.key.as_ref()
}
pub fn value(&self) -> Result<Cow<str>, Error>
{
self.inner.unescape_value().map_err(|err| match err {
QuickXMLError::NonDecodable(Some(utf8_error)) => {
Error::ValueNotUTF8(utf8_error)
}
QuickXMLError::EscapeError(escape_err) => {
Error::UnescapeValueFailed(EscapeError::from_quick_xml(escape_err))
}
_ => {
unreachable!();
}
})
}
#[must_use]
pub fn value_bytes(&self) -> &[u8]
{
&self.inner.value
}
}
impl<'a> Attribute<'a>
{
pub(crate) fn from_inner(inner: QuickXMLAttribute<'a>) -> Self
{
Self { inner }
}
pub(crate) fn into_inner(self) -> QuickXMLAttribute<'a>
{
self.inner
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error
{
#[error("Position {0}: attribute key must be directly followed by `=` or space")]
ExpectedEq(usize),
#[error("Position {0}: `=` must be followed by an attribute value")]
ExpectedValue(usize),
#[error("Position {0}: attribute value must be enclosed in `\"` or `'`")]
UnquotedValue(usize),
#[error("Position {0}: missing closing quote `{1}` in attribute value")]
ExpectedQuote(usize, u8),
#[error("Position {0}: duplicated attribute, previous declaration at position {1}")]
Duplicated(usize, usize),
#[error("Attribute key is not valid UTF-8")]
KeyNotUTF8(#[source] Utf8Error),
#[error("Attribute value is not valid UTF-8")]
ValueNotUTF8(#[source] Utf8Error),
#[error("Failed to unescape value")]
UnescapeValueFailed(#[source] EscapeError),
}
impl From<AttrError> for Error
{
fn from(attr_err: AttrError) -> Self
{
match attr_err {
AttrError::ExpectedEq(pos) => Self::ExpectedEq(pos),
AttrError::ExpectedValue(pos) => Self::ExpectedValue(pos),
AttrError::UnquotedValue(pos) => Self::UnquotedValue(pos),
AttrError::ExpectedQuote(pos, quote) => Self::ExpectedQuote(pos, quote),
AttrError::Duplicated(pos, same_attr_pos) => {
Self::Duplicated(pos, same_attr_pos)
}
}
}
}
#[derive(Debug)]
pub struct Iter<'a>
{
attrs: Attributes<'a>,
}
impl<'a> Iter<'a>
{
pub(crate) fn new(attrs: Attributes<'a>) -> Self
{
Self { attrs }
}
}
impl<'a> Iterator for Iter<'a>
{
type Item = Result<Attribute<'a>, Error>;
fn next(&mut self) -> Option<Self::Item>
{
let attr = self.attrs.next()?;
Some(attr.map(Attribute::from_inner).map_err(Into::into))
}
}