use crate::{
node::{Doctype, NodeData, ProcessingInstruction},
HtmlStr, NodeKind, Selector,
};
use ego_tree::{
iter::{Edge, Traverse},
NodeRef,
};
use html5ever::serialize::{serialize, SerializeOpts, TraversalScope};
use std::fmt::{Debug, Formatter};
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Node<'a> {
pub(crate) ptr: NodeRef<'a, NodeKind>,
}
impl<'a> Debug for Node<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ElementHandler").field(&self.ptr.id()).finish()
}
}
impl<'a> Node<'a> {
fn new(node: NodeRef<'a, NodeKind>) -> Self {
Node { ptr: node }
}
pub fn wrap(node: NodeRef<'a, NodeKind>) -> Option<Self> {
if node.value().is_element() { Some(Node::new(node)) } else { None }
}
pub fn select<'b>(&self, selector: &'b Selector) -> Select<'a, 'b> {
let mut inner = self.ptr.traverse();
inner.next();
Select { scope: *self, inner, selector }
}
fn serialize(&self, traversal_scope: TraversalScope) -> std::io::Result<String> {
let opts = SerializeOpts {
scripting_enabled: true, traversal_scope,
create_missing_parent: false,
};
let mut buf = Vec::new();
serialize(&mut buf, self, opts)?;
unsafe { Ok(String::from_utf8_unchecked(buf)) }
}
pub fn text(&self) -> Text<'a> {
Text { inner: self.ptr.traverse() }
}
pub fn children(&self) -> impl Iterator<Item = Node<'a>> {
self.ptr.children().map(Node::new)
}
pub fn first_child(&self) -> Option<Node<'a>> {
self.ptr.first_child().map(Node::new)
}
pub fn last_child(&self) -> Option<Node<'a>> {
self.ptr.last_child().map(Node::new)
}
pub fn descendants(&self) -> impl Iterator<Item = Node<'a>> {
self.ptr.descendants().map(Node::new)
}
pub fn has_class(&self, class: &str) -> bool {
match self.as_element() {
Some(data) => data.has_class(class),
None => false,
}
}
pub fn has_attribute(&self, name: &str) -> bool {
match self.as_element() {
Some(data) => data.has_attribute(name),
None => false,
}
}
pub fn get_attribute(&self, name: &str) -> &'a str {
self.as_element().and_then(|data| data.get_attribute(name)).unwrap_or("")
}
}
impl<'a> Node<'a> {
pub fn is_a<S>(&self, element: S) -> bool
where
S: AsRef<str>,
{
match self.as_element() {
Some(data) => data.is_a(element.as_ref()),
None => false,
}
}
pub fn as_kind(&self) -> &'a NodeKind {
self.ptr.value()
}
pub fn as_element(&self) -> Option<&'a NodeData> {
match self.as_kind() {
NodeKind::Element(ref e) => Some(e),
_ => None,
}
}
pub fn as_doctype(&self) -> Option<&'a Doctype> {
match self.as_kind() {
NodeKind::Doctype(t) => Some(t),
_ => None,
}
}
pub fn as_text(&self) -> Option<&'a HtmlStr> {
match self.as_kind() {
NodeKind::Text(t) => Some(t),
_ => None,
}
}
pub fn as_html(&self) -> String {
self.serialize(TraversalScope::IncludeNode).unwrap()
}
pub fn inner_html(&self) -> String {
self.serialize(TraversalScope::ChildrenOnly(None)).unwrap()
}
pub fn as_processing_instruction(&self) -> Option<&ProcessingInstruction> {
match self.as_kind() {
NodeKind::ProcessingInstruction(ref pi) => Some(pi),
_ => None,
}
}
pub fn as_comment(&self) -> Option<&'a HtmlStr> {
match self.as_kind() {
NodeKind::Comment(t) => Some(t),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct Select<'a, 'b> {
scope: Node<'a>,
inner: Traverse<'a, NodeKind>,
selector: &'b Selector,
}
impl<'a, 'b> Iterator for Select<'a, 'b> {
type Item = Node<'a>;
fn next(&mut self) -> Option<Node<'a>> {
for edge in &mut self.inner {
if let Edge::Open(node) = edge {
if let Some(element) = Node::wrap(node) {
if self.selector.matches_with_scope(&element, Some(self.scope)) {
return Some(element);
}
}
}
}
None
}
}
#[derive(Debug, Clone)]
pub struct Text<'a> {
inner: Traverse<'a, NodeKind>,
}
impl<'a> Iterator for Text<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<&'a str> {
for edge in &mut self.inner {
if let Edge::Open(node) = edge {
if let NodeKind::Text(ref text) = node.value() {
return Some(&**text);
}
}
}
None
}
}
mod element;
mod serializable;
#[cfg(test)]
mod tests {
use crate::{html::Html, selector::Selector};
#[test]
fn test_scope() {
let html = r"
<div>
<b>1</b>
<span>
<span><b>2</b></span>
<b>3</b>
</span>
</div>
";
let fragment = Html::parse_fragment(html);
let sel1 = Selector::try_parse("div > span").unwrap();
let sel2 = Selector::try_parse(":scope > b").unwrap();
let element1 = fragment.select(&sel1).next().unwrap();
let element2 = element1.select(&sel2).next().unwrap();
assert_eq!(element2.inner_html(), "3");
}
}