xml_stinks/
tagged.rs

1//! Tagged element.
2
3use std::borrow::Cow;
4use std::str::Utf8Error;
5
6use quick_xml::events::BytesStart;
7
8use crate::attribute::{Attribute, Error as AttributeError, Iter as AttributeIter};
9
10/// The start tag of a tagged element.
11///
12/// The `<xyz foo="bar">` in `<xyz foo="bar">Hello</xyz>`
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct TagStart<'a>
15{
16    inner: BytesStart<'a>,
17}
18
19impl<'a> TagStart<'a>
20{
21    /// Returns a new `TagStart`.
22    pub fn new(name: impl Into<Cow<'a, str>>) -> Self
23    {
24        Self {
25            inner: BytesStart::new(name),
26        }
27    }
28
29    /// Returns `Self` with the specified attributes.
30    #[must_use]
31    pub fn with_attributes<Attrs>(mut self, attrs: Attrs) -> Self
32    where
33        Attrs: IntoIterator<Item = Attribute<'a>>,
34    {
35        self.inner = self
36            .inner
37            .with_attributes(attrs.into_iter().map(Attribute::into_inner));
38
39        self
40    }
41
42    /// Returns the name.
43    ///
44    /// # Errors
45    /// Returns `Err` if the name is not valid UTF-8.
46    pub fn name(&self) -> Result<&str, TagStartError>
47    {
48        std::str::from_utf8(self.name_bytes()).map_err(TagStartError::NameNotUTF8)
49    }
50
51    /// Returns the name as bytes.
52    #[must_use]
53    pub fn name_bytes(&self) -> &[u8]
54    {
55        let name_length = self.inner.name().as_ref().len();
56
57        &self.inner.as_ref()[..name_length]
58    }
59
60    /// Returns the tag attributes.
61    #[must_use]
62    pub fn attributes(&'a self) -> AttributeIter<'a>
63    {
64        AttributeIter::new(self.inner.attributes())
65    }
66
67    /// Returns a attribute.
68    ///
69    /// # Errors
70    /// Returns `Err` if a invalid attribute is found.
71    pub fn get_attribute(
72        &self,
73        attr_name: &str,
74    ) -> Result<Option<Attribute>, TagStartError>
75    {
76        for attr_result in self.inner.attributes().with_checks(false) {
77            let attr = attr_result.map_err(AttributeError::from)?;
78
79            if attr.key.as_ref() == attr_name.as_bytes() {
80                return Ok(Some(Attribute::from_inner(attr)));
81            }
82        }
83
84        Ok(None)
85    }
86}
87
88// Crate-local functions
89impl<'a> TagStart<'a>
90{
91    pub(crate) fn from_inner(inner: BytesStart<'a>) -> Self
92    {
93        Self { inner }
94    }
95}
96
97/// `TagStart` error.
98#[derive(Debug, thiserror::Error)]
99pub enum TagStartError
100{
101    /// Invalid attribute.
102    #[error("Invalid attribute")]
103    InvalidAttribute(#[from] AttributeError),
104
105    /// Name is not valid UTF-8.
106    #[error("Name is not valid UTF-8")]
107    NameNotUTF8(#[source] Utf8Error),
108}