hatmel/
navigate.rs

1use super::*;
2use std::collections::VecDeque;
3
4impl Hatmel {
5    pub fn get(&self, handle: Handle) -> Option<&Content> {
6        self.nodes.get(handle).map(|n| &n.content)
7    }
8    /// get all direct child nodes
9    pub fn children(&self, handle: Handle) -> impl DoubleEndedIterator<Item = Handle> + '_ {
10        self.children
11            .get(&handle)
12            .map(|v| &v[..])
13            .unwrap_or_default()
14            .iter()
15            .cloned()
16    }
17    /// get the parent node which may be either an element or document root
18    pub fn parent(&self, handle: Handle) -> Option<Handle> {
19        self.parents.get(&handle).cloned()
20    }
21    pub fn ancestors(&self, adept: Handle) -> ParentIter {
22        ParentIter::new(self, adept)
23    }
24    /// if the node is an element returns the local name and namespace
25    pub fn get_element_name(&self, node: Handle) -> Option<(&str, &str)> {
26        self.nodes.get(node)?.content.element_name()
27    }
28    /// get the last (overriding) attribute value if it's an element
29    pub fn get_element_attribute(&self, node: Handle, name: &str, ns: &str) -> Option<&str> {
30        let attrs = self.nodes.get(node)?.content.attributes();
31        attrs
32            .rev()
33            .filter(|a| &a.name == name && namespace_equals_or_html(&a.namespace, ns))
34            .map(|a| a.value.as_ref())
35            .next()
36    }
37    /// get following adjacent nodes, whether or not they are elements
38    pub fn siblings_after(&self, handle: Handle) -> impl DoubleEndedIterator<Item = Handle> + '_ {
39        self.parent(handle)
40            .and_then(|p| self.children.get(&p).map(|all| &all[..]))
41            .and_then(|all| {
42                all.iter()
43                    .position(|c| c == &handle)
44                    .map(|mypos| &all[mypos.saturating_add(1)..])
45            })
46            .unwrap_or_default()
47            .iter()
48            .cloned()
49    }
50    /// get preceeding adjacent nodes, whether or not they are elements
51    pub fn siblings_before(&self, handle: Handle) -> impl DoubleEndedIterator<Item = Handle> + '_ {
52        self.parent(handle)
53            .and_then(|p| self.children.get(&p).map(|all| &all[..]))
54            .and_then(|all| {
55                all.iter()
56                    .position(|c| c == &handle)
57                    .map(|mypos| &all[..mypos])
58            })
59            .unwrap_or_default()
60            .iter()
61            .cloned()
62    }
63    /// get the document title if it exists
64    pub fn get_title(&self) -> StrTendril {
65        let title: Option<StrTendril> = (|| {
66            let html = self
67                .as_element(0, "html", "")
68                .or_else(|| self.children_as_elements(0, "html", "").into_iter().next())?;
69            let head = self
70                .children_as_elements(html, "head", "")
71                .into_iter()
72                .next()?;
73            let title = self
74                .children_as_elements(head, "title", "")
75                .into_iter()
76                .next()?;
77            Some(self.get_text(title))
78        })();
79        title.unwrap_or_default()
80    }
81
82    fn as_element(&self, node: Handle, local_name: &str, name_space: &str) -> Option<Handle> {
83        let (ln, ns) = self.get_element_name(node)?;
84        if local_name != ln {
85            return None;
86        }
87        if !namespace_equals_or_html(name_space, ns) {
88            return None;
89        }
90        Some(node)
91    }
92    fn children_as_elements<'a>(
93        &'a self,
94        node: Handle,
95        local_name: &'a str,
96        name_space: &'a str,
97    ) -> impl IntoIterator<Item = Handle> + 'a {
98        self.children[&node]
99            .iter()
100            .filter_map(move |c| self.as_element(*c, local_name, name_space))
101    }
102    /// get node text including children
103    pub fn get_text(&self, node: Handle) -> StrTendril {
104        let mut s = StrTendril::new();
105
106        let mut nodes = VecDeque::from(vec![node]);
107        while let Some(node) = nodes.pop_front() {
108            match &self.nodes[node].content {
109                // these nodes do not contain TEXT
110                Content::Comment { .. }
111                | Content::ProcessingInstruction { .. }
112                | Content::DocType { .. }
113                | Content::Element(Element {
114                    kind: ElementKind::Template,
115                    ..
116                }) => {}
117
118                // Nodes that may contain text
119                Content::Element { .. } | Content::Document => {
120                    // descend into children - depth first
121                    nodes = self
122                        .children(node)
123                        .chain(nodes.into_iter())
124                        .collect::<Vec<Handle>>()
125                        .into()
126                }
127
128                // This is text
129                Content::Text { text } => s.push_slice(text.as_str()),
130            }
131        }
132        s
133    }
134}
135
136#[derive(Debug)]
137
138pub struct ParentIter<'a> {
139    adept: Option<Handle>,
140    doc: &'a Hatmel,
141}
142impl<'a> ParentIter<'a> {
143    fn new(doc: &'a Hatmel, adept: Handle) -> Self {
144        Self {
145            doc,
146            adept: Some(adept),
147        }
148    }
149}
150impl<'a> Iterator for ParentIter<'a> {
151    type Item = Handle;
152
153    fn next(&mut self) -> Option<Self::Item> {
154        if let Some(adept) = self.adept {
155            self.adept = self.doc.parent(adept);
156        }
157        self.adept
158    }
159}
160
161#[derive(Debug, Default, Clone)]
162pub struct HandleIter<'a> {
163    curr: Arc<AtomicUsize>,
164    end: Handle,
165    // pretend we depend on the doc to prevent modification
166    phantom: PhantomData<Cow<'a, ()>>,
167}
168impl<'a> HandleIter<'a> {
169    pub(crate) fn new(range: std::ops::Range<usize>) -> Self {
170        Self {
171            curr: Arc::new(AtomicUsize::new(range.start)),
172            end: range.end,
173            phantom: PhantomData,
174        }
175    }
176}
177impl<'a> Iterator for HandleIter<'a> {
178    type Item = Handle;
179
180    fn next(&mut self) -> Option<Self::Item> {
181        if self.curr.load(std::sync::atomic::Ordering::SeqCst) >= self.end {
182            return None;
183        }
184        let next = self
185            .curr
186            .as_ref()
187            .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
188        if next >= self.end {
189            return None;
190        }
191        Some(next)
192    }
193}