//! Attribute.
use quick_xml::events::attributes::{
AttrError,
Attribute as QuickXMLAttribute,
Attributes,
};
/// Represent a XML attribute.
#[derive(Debug, Clone, PartialEq)]
pub struct Attribute<'a>
{
inner: QuickXMLAttribute<'a>,
}
impl<'a> Attribute<'a>
{
/// Attribute key.
#[must_use]
pub fn key(&self) -> &[u8]
{
self.inner.key.as_ref()
}
/// Attribute value.
#[must_use]
pub fn value(&self) -> &[u8]
{
&self.inner.value
}
}
// Crate-local functions
impl<'a> Attribute<'a>
{
pub(crate) fn from_inner(inner: QuickXMLAttribute<'a>) -> Self
{
Self { inner }
}
}
/// Errors that can be raised when parsing [`Attribute`]s.
///
/// Recovery position in examples shows the position from which parsing of the
/// next attribute will be attempted.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error
{
/// Attribute key was not followed by `=`, position relative to the start of
/// the owning tag is provided.
///
/// Example of input that raises this error:
/// ```xml
///
///
/// ```
#[error("Position {0}: attribute key must be directly followed by `=` or space")]
ExpectedEq(usize),
/// Attribute value was not found after `=`, position relative to the start
/// of the owning tag is provided.
///
/// Example of input that raises this error:
/// ```xml
///
///
/// ```
///
/// This error can be returned only for the last attribute in the list,
/// because otherwise any content after `=` will be threated as a value.
/// The XML
/// ```xml
///
///
///
/// ```
///
/// will be treated as `Attribute { key = b"key", value = b"another-key" }`
/// and or [`Attribute`] is returned, or [`Error::UnquotedValue`] is raised,
/// depending on the parsing mode.
#[error("Position {0}: `=` must be followed by an attribute value")]
ExpectedValue(usize),
/// Attribute value is not quoted, position relative to the start of the
/// owning tag is provided.
///
/// Example of input that raises this error:
/// ```xml
///
///
///
/// ```
#[error("Position {0}: attribute value must be enclosed in `\"` or `'`")]
UnquotedValue(usize),
/// Attribute value was not finished with a matching quote, position relative
/// to the start of owning tag and a quote is provided. That position is always
/// a last character in the tag content.
///
/// Example of input that raises this error:
/// ```xml
///
///
///
///
/// ```
#[error("Position {0}: duplicated attribute, previous declaration at position {1}")]
Duplicated(usize, usize),
}
impl From 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)
}
}
}
}
/// Iterates through [`Attribute`]s.
#[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, Error>;
fn next(&mut self) -> Option
{
let attr = self.attrs.next()?;
Some(attr.map(Attribute::from_inner).map_err(Into::into))
}
}