From e762babd9e69400ccd178ba8946168640093eb63 Mon Sep 17 00:00:00 2001 From: HampusM Date: Sat, 15 Apr 2023 18:26:29 +0200 Subject: feat: add deserialization --- src/attribute.rs | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 src/attribute.rs (limited to 'src/attribute.rs') diff --git a/src/attribute.rs b/src/attribute.rs new file mode 100644 index 0000000..8fb4778 --- /dev/null +++ b/src/attribute.rs @@ -0,0 +1,170 @@ +//! 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)) + } +} -- cgit v1.2.3-18-g5258