use crate::prelude::*;
use super::Nodes;
#[derive(Debug, Clone)]
pub struct Node<'a> {
element: scraper::ElementRef<'a>,
selector: scraper::Selector
}
impl<'a> Node<'a> {
pub(crate) fn new(element: scraper::ElementRef<'a>) -> Self {
Self {
element,
selector: scraper::Selector::parse("*").unwrap()
}
}
pub fn select(&self, selector: &'static str) -> Result<Option<Node<'a>>> {
let sel = scraper::Selector::parse(selector).map_err(Error::from)?;
let node = self.element
.select(&sel)
.next()
.map(Node::new);
Ok(node)
}
pub fn select_all(&mut self, selector: &'static str) -> Result<Option<Nodes>> {
self.selector = scraper::Selector::parse(selector).map_err(Error::from)?;
let mut nodes = self.element.select(&self.selector).peekable();
if nodes.peek().is_some() {
Ok(Some(Nodes::new(None, Some(nodes))))
} else {
Ok(None)
}
}
pub fn parent(&self) -> Option<Node<'a>> {
self.element.parent()
.and_then(|node| scraper::ElementRef::wrap(node))
.map(Node::new)
}
pub fn attr(&self, name: &str) -> Option<&str> {
self.element.value().attr(name)
}
pub fn text(&self) -> String {
self.element.text().collect()
}
pub fn html(&self) -> String {
self.element.html()
}
pub fn filter_text(&self, black_list: &[&str]) -> String {
Self::filter_elem_text(self.element, black_list)
.split_whitespace()
.collect::<Vec<_>>()
.join(" ")
}
fn filter_elem_text(node: scraper::element_ref::ElementRef, black_list: &[&str]) -> String {
let tag_name = node.value().name();
if black_list.contains(&tag_name) {
return String::new();
}
let mut result = String::new();
for child in node.children() {
match child.value() {
scraper::node::Node::Text(text) => {
result.push(' ');
result.push_str(text);
}
scraper::node::Node::Element(_) => {
if let Some(child_element) = scraper::ElementRef::wrap(child) {
result.push(' ');
result.push_str(&Self::filter_elem_text(child_element, black_list));
}
}
_ => {}
}
}
result
}
}