use super::{ElementList, get_selector, re_matches};
static ROOT_SELECTOR: std::sync::LazyLock<scraper::Selector> =
std::sync::LazyLock::new(|| scraper::Selector::parse("*").unwrap());
#[derive(Clone, Debug)]
pub struct Element {
pub(crate) outer_html: String,
}
impl Element {
pub fn text(&self) -> String {
let fragment = scraper::Html::parse_fragment(&self.outer_html);
fragment.root_element().text().collect::<Vec<_>>().join("")
}
pub fn attr(&self, name: &str) -> Option<String> {
let fragment = scraper::Html::parse_fragment(&self.outer_html);
fragment
.select(&ROOT_SELECTOR)
.find(|el| !matches!(el.value().name(), "html" | "body"))
.and_then(|el| el.value().attr(name))
.map(String::from)
}
pub fn css(&self, selector: &str) -> ElementList {
let fragment = scraper::Html::parse_fragment(&self.outer_html);
let Some(sel) = get_selector(selector) else {
return ElementList { elements: vec![] };
};
let elements = fragment
.select(&sel)
.map(|el| Element {
outer_html: el.html(),
})
.collect();
ElementList { elements }
}
pub fn re(&self, pattern: &str) -> Vec<String> {
re_matches(&self.text(), pattern)
}
pub fn re_first(&self, pattern: &str) -> Option<String> {
self.re(pattern).into_iter().next()
}
pub fn outer_html(&self) -> &str {
&self.outer_html
}
pub fn inner_html(&self) -> String {
let fragment = scraper::Html::parse_fragment(&self.outer_html);
fragment
.select(&ROOT_SELECTOR)
.find(|el| !matches!(el.value().name(), "html" | "body"))
.map(|el| el.inner_html())
.unwrap_or_default()
}
}