hatmel 0.2.0

HTML model and parser (html5ever)
Documentation
use super::*;
use std::collections::VecDeque;

impl Hatmel {
    pub fn get(&self, handle: Handle) -> Option<&Content> {
        self.nodes.get(handle).map(|n| &n.content)
    }
    /// get all direct child nodes
    pub fn children(&self, handle: Handle) -> impl DoubleEndedIterator<Item = Handle> + '_ {
        self.children
            .get(&handle)
            .map(|v| &v[..])
            .unwrap_or_default()
            .iter()
            .cloned()
    }
    /// get the parent node which may be either an element or document root
    pub fn parent(&self, handle: Handle) -> Option<Handle> {
        self.parents.get(&handle).cloned()
    }
    pub fn ancestors(&self, adept: Handle) -> ParentIter {
        ParentIter::new(self, adept)
    }
    /// if the node is an element returns the local name and namespace
    pub fn get_element_name(&self, node: Handle) -> Option<(&str, &str)> {
        self.nodes.get(node)?.content.element_name()
    }
    /// get the last (overriding) attribute value if it's an element
    pub fn get_element_attribute(&self, node: Handle, name: &str, ns: &str) -> Option<&str> {
        let attrs = self.nodes.get(node)?.content.attributes();
        attrs
            .rev()
            .filter(|a| &a.name == name && namespace_equals_or_html(&a.namespace, ns))
            .map(|a| a.value.as_ref())
            .next()
    }
    /// get following adjacent nodes, whether or not they are elements
    pub fn siblings_after(&self, handle: Handle) -> impl DoubleEndedIterator<Item = Handle> + '_ {
        self.parent(handle)
            .and_then(|p| self.children.get(&p).map(|all| &all[..]))
            .and_then(|all| {
                all.iter()
                    .position(|c| c == &handle)
                    .map(|mypos| &all[mypos.saturating_add(1)..])
            })
            .unwrap_or_default()
            .iter()
            .cloned()
    }
    /// get preceeding adjacent nodes, whether or not they are elements
    pub fn siblings_before(&self, handle: Handle) -> impl DoubleEndedIterator<Item = Handle> + '_ {
        self.parent(handle)
            .and_then(|p| self.children.get(&p).map(|all| &all[..]))
            .and_then(|all| {
                all.iter()
                    .position(|c| c == &handle)
                    .map(|mypos| &all[..mypos])
            })
            .unwrap_or_default()
            .iter()
            .cloned()
    }
    /// get the document title if it exists
    pub fn get_title(&self) -> StrTendril {
        let title: Option<StrTendril> = (|| {
            let html = self
                .as_element(0, "html", "")
                .or_else(|| self.children_as_elements(0, "html", "").into_iter().next())?;
            let head = self
                .children_as_elements(html, "head", "")
                .into_iter()
                .next()?;
            let title = self
                .children_as_elements(head, "title", "")
                .into_iter()
                .next()?;
            Some(self.get_text(title))
        })();
        title.unwrap_or_default()
    }

    fn as_element(&self, node: Handle, local_name: &str, name_space: &str) -> Option<Handle> {
        let (ln, ns) = self.get_element_name(node)?;
        if local_name != ln {
            return None;
        }
        if !namespace_equals_or_html(name_space, ns) {
            return None;
        }
        Some(node)
    }
    fn children_as_elements<'a>(
        &'a self,
        node: Handle,
        local_name: &'a str,
        name_space: &'a str,
    ) -> impl IntoIterator<Item = Handle> + 'a {
        self.children[&node]
            .iter()
            .filter_map(move |c| self.as_element(*c, local_name, name_space))
    }
    /// get node text including children
    pub fn get_text(&self, node: Handle) -> StrTendril {
        let mut s = StrTendril::new();

        let mut nodes = VecDeque::from(vec![node]);
        while let Some(node) = nodes.pop_front() {
            match &self.nodes[node].content {
                // these nodes do not contain TEXT
                Content::Comment { .. }
                | Content::ProcessingInstruction { .. }
                | Content::DocType { .. }
                | Content::Element(Element {
                    kind: ElementKind::Template,
                    ..
                }) => {}

                // Nodes that may contain text
                Content::Element { .. } | Content::Document => {
                    // descend into children - depth first
                    nodes = self
                        .children(node)
                        .chain(nodes.into_iter())
                        .collect::<Vec<Handle>>()
                        .into()
                }

                // This is text
                Content::Text { text } => s.push_slice(text.as_str()),
            }
        }
        s
    }
}

#[derive(Debug)]

pub struct ParentIter<'a> {
    adept: Option<Handle>,
    doc: &'a Hatmel,
}
impl<'a> ParentIter<'a> {
    fn new(doc: &'a Hatmel, adept: Handle) -> Self {
        Self {
            doc,
            adept: Some(adept),
        }
    }
}
impl<'a> Iterator for ParentIter<'a> {
    type Item = Handle;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(adept) = self.adept {
            self.adept = self.doc.parent(adept);
        }
        self.adept
    }
}

#[derive(Debug, Default, Clone)]
pub struct HandleIter<'a> {
    curr: Arc<AtomicUsize>,
    end: Handle,
    // pretend we depend on the doc to prevent modification
    phantom: PhantomData<Cow<'a, ()>>,
}
impl<'a> HandleIter<'a> {
    pub(crate) fn new(range: std::ops::Range<usize>) -> Self {
        Self {
            curr: Arc::new(AtomicUsize::new(range.start)),
            end: range.end,
            phantom: PhantomData,
        }
    }
}
impl<'a> Iterator for HandleIter<'a> {
    type Item = Handle;

    fn next(&mut self) -> Option<Self::Item> {
        if self.curr.load(std::sync::atomic::Ordering::SeqCst) >= self.end {
            return None;
        }
        let next = self
            .curr
            .as_ref()
            .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
        if next >= self.end {
            return None;
        }
        Some(next)
    }
}