Skip to main content

robinson/
attributes.rs

1use std::fmt;
2use std::slice::Iter;
3
4use crate::{
5    Document, Name, NameData,
6    nodes::{Node, NodeId},
7};
8
9impl<'doc, 'input> Node<'doc, 'input> {
10    pub fn has_attributes(self) -> bool {
11        self.element_data()
12            .is_some_and(|element| element.attributes_len != 0)
13    }
14
15    pub fn attributes(self) -> Attributes<'doc, 'input> {
16        let data = self
17            .element_data()
18            .map(|element| {
19                let start = element.attributes_start as usize;
20                let end = start + element.attributes_len as usize;
21
22                &self.doc.attributes[start..end]
23            })
24            .unwrap_or_default();
25
26        Attributes {
27            data: data.iter(),
28            doc: self.doc,
29        }
30    }
31
32    /// ```
33    /// # use robinson::Document;
34    /// let doc = Document::parse(r#"<root foo="bar" baz="qux"/>"#).unwrap();
35    ///
36    /// let baz = doc.root_element().attribute("baz");
37    ///
38    /// assert_eq!(baz, Some("qux"));
39    pub fn attribute<N>(self, name: N) -> Option<&'doc str>
40    where
41        Name<'doc, 'input>: PartialEq<N>,
42    {
43        self.attributes()
44            .find(|attribute| attribute.name() == name)
45            .map(|attribute| attribute.value())
46    }
47}
48
49#[derive(Clone, Copy)]
50pub struct Attribute<'doc, 'input> {
51    data: &'doc AttributeData<'input>,
52    doc: &'doc Document<'input>,
53}
54
55impl<'doc, 'input> Attribute<'doc, 'input> {
56    pub fn document(self) -> &'doc Document<'input> {
57        self.doc
58    }
59
60    pub fn name(self) -> Name<'doc, 'input> {
61        self.data.name.get(self.doc)
62    }
63
64    pub fn value(self) -> &'doc str {
65        self.doc.strings.get(self.data.value)
66    }
67}
68
69#[repr(Rust, packed)]
70pub(crate) struct AttributeData<'input> {
71    pub(crate) name: NameData<'input>,
72    pub(crate) value: NodeId,
73}
74
75const _SIZE_OF_ATTRIBUTE_DATA: () = assert!(
76    size_of::<AttributeData<'static>>()
77        == size_of::<u16>() + 2 * size_of::<usize>() + size_of::<u32>()
78);
79
80#[derive(Clone)]
81pub struct Attributes<'doc, 'input> {
82    data: Iter<'doc, AttributeData<'input>>,
83    doc: &'doc Document<'input>,
84}
85
86impl<'doc, 'input> Attributes<'doc, 'input> {
87    fn get(&self, data: &'doc AttributeData<'input>) -> Attribute<'doc, 'input> {
88        Attribute {
89            data,
90            doc: self.doc,
91        }
92    }
93}
94
95impl<'doc, 'input> Iterator for Attributes<'doc, 'input> {
96    type Item = Attribute<'doc, 'input>;
97
98    fn next(&mut self) -> Option<Self::Item> {
99        self.data.next().map(|data| self.get(data))
100    }
101
102    fn nth(&mut self, n: usize) -> Option<Self::Item> {
103        self.data.nth(n).map(|data| self.get(data))
104    }
105
106    fn size_hint(&self) -> (usize, Option<usize>) {
107        self.data.size_hint()
108    }
109}
110
111impl ExactSizeIterator for Attributes<'_, '_> {}
112
113impl DoubleEndedIterator for Attributes<'_, '_> {
114    fn next_back(&mut self) -> Option<Self::Item> {
115        self.data.next_back().map(|data| self.get(data))
116    }
117}
118
119impl fmt::Debug for Attribute<'_, '_> {
120    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
121        fmt.debug_struct("Attribute")
122            .field("name", &self.name())
123            .field("value", &self.value())
124            .finish()
125    }
126}
127
128impl fmt::Debug for Attributes<'_, '_> {
129    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
130        let mut fmt = fmt.debug_map();
131
132        for attribute in self.clone() {
133            fmt.entry(&attribute.name(), &attribute.value());
134        }
135
136        fmt.finish()
137    }
138}