use std::cell::{Ref, RefCell, RefMut};
use std::collections::HashMap;
use std::fmt;
use std::rc::{Rc, Weak};
use crate::{is_void_element, Html, IterableNodes};
type Link = Rc<RefCell<DomNodeData>>;
type WeakLink = Weak<RefCell<DomNodeData>>;
pub struct DomNode(Link);
pub struct WeakDomNode(WeakLink);
#[derive(Debug, Clone, PartialEq)]
pub enum DomNodeKind {
Text {
text: String,
},
Element {
tag: String,
attributes: HashMap<String, String>,
},
}
struct DomNodeData {
kind: DomNodeKind,
parent: Option<WeakLink>,
first_child: Option<Link>,
last_child: Option<WeakLink>,
previous_sibling: Option<WeakLink>,
next_sibling: Option<Link>,
}
impl Clone for DomNode {
fn clone(&self) -> Self {
DomNode(Rc::clone(&self.0))
}
}
impl PartialEq for DomNode {
fn eq(&self, other: &DomNode) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
}
impl fmt::Debug for DomNode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.kind(), f)
}
}
impl DomNode {
pub fn new(kind: DomNodeKind) -> DomNode {
DomNode(Rc::new(RefCell::new(DomNodeData {
kind,
parent: None,
first_child: None,
last_child: None,
previous_sibling: None,
next_sibling: None,
})))
}
pub fn create_element(tag: impl Into<String>) -> DomNode {
Self::new(DomNodeKind::Element {
tag: tag.into(),
attributes: HashMap::new(),
})
}
pub fn create_element_with_attributes(
tag: impl Into<String>,
attributes: HashMap<String, String>,
) -> DomNode {
Self::new(DomNodeKind::Element {
tag: tag.into(),
attributes,
})
}
pub fn create_text(text: impl Into<String>) -> DomNode {
Self::new(DomNodeKind::Text { text: text.into() })
}
pub fn get_attribute(&self, attribute: &str) -> Option<String> {
match &*self.kind() {
DomNodeKind::Text { .. } => None,
DomNodeKind::Element { attributes, .. } => attributes.get(attribute).cloned(),
}
}
pub fn set_attribute(&mut self, key: &str, value: &str) {
if let DomNodeKind::Element { attributes, .. } = &mut *self.kind_mut() {
attributes.insert(key.into(), value.into());
}
}
pub fn downgrade(&self) -> WeakDomNode {
WeakDomNode(Rc::downgrade(&self.0))
}
pub fn parent(&self) -> Option<DomNode> {
Some(DomNode(self.0.borrow().parent.as_ref()?.upgrade()?))
}
pub fn first_child(&self) -> Option<DomNode> {
Some(DomNode(self.0.borrow().first_child.as_ref()?.clone()))
}
pub fn last_child(&self) -> Option<DomNode> {
Some(DomNode(self.0.borrow().last_child.as_ref()?.upgrade()?))
}
pub fn previous_sibling(&self) -> Option<DomNode> {
Some(DomNode(
self.0.borrow().previous_sibling.as_ref()?.upgrade()?,
))
}
pub fn next_sibling(&self) -> Option<DomNode> {
Some(DomNode(self.0.borrow().next_sibling.as_ref()?.clone()))
}
pub fn kind(&self) -> Ref<'_, DomNodeKind> {
Ref::map(self.0.borrow(), |v| &v.kind)
}
pub fn kind_mut(&self) -> RefMut<'_, DomNodeKind> {
RefMut::map(self.0.borrow_mut(), |v| &mut v.kind)
}
pub fn ancestors(&self) -> Ancestors {
Ancestors(Some(self.clone()))
}
pub fn preceding_siblings(&self) -> PrecedingSiblings {
PrecedingSiblings(Some(self.clone()))
}
pub fn following_siblings(&self) -> FollowingSiblings {
FollowingSiblings(Some(self.clone()))
}
pub fn children(&self) -> Children {
Children {
next: self.first_child(),
next_back: self.last_child(),
}
}
pub fn has_children(&self) -> bool {
self.first_child().is_some()
}
pub fn descendants(&self) -> Descendants {
Descendants(self.traverse())
}
pub fn traverse(&self) -> Traverse {
Traverse {
root: self.clone(),
next: Some(NodeEdge::Start(self.clone())),
next_back: Some(NodeEdge::End(self.clone())),
}
}
pub fn sanitize_children(&mut self) {
for mut c in self.children() {
c.sanitize_children();
}
let should_remove = match &*self.kind() {
DomNodeKind::Text { text } if text.is_empty() => true,
DomNodeKind::Element { tag, .. } if tag == "p" && self.first_child().is_none() => true,
DomNodeKind::Element { tag, .. } if tag == "p" => self.children().all(|c| {
if let DomNodeKind::Text { text } = &*c.kind() {
text.chars().all(|c| c == ' ')
} else {
false
}
}),
_ => false,
};
if should_remove {
self.detach();
return;
}
match &mut *self.kind_mut() {
DomNodeKind::Text { text } if text.chars().all(|c| c == ' ') => *text = " ".into(),
_ => {}
};
}
pub fn get_elements_by_tag_name(&self, tag: &str) -> Vec<DomNode> {
self.descendants()
.filter(|d| {
if let DomNodeKind::Element { tag: t, .. } = &*d.kind() {
if t == tag {
return true;
}
}
false
})
.collect()
}
pub fn get_element_by_id(&self, id: &str) -> Option<DomNode> {
self.descendants().find(|d| {
if let DomNodeKind::Element { attributes, .. } = &*d.kind() {
if let Some(element_id) = attributes.get("id") {
return element_id == id;
}
}
false
})
}
pub fn inner_text(&self) -> String {
let mut text = String::new();
for descendant in self.descendants() {
if let DomNodeKind::Text { text: t } = &*descendant.kind() {
text.push_str(t);
}
}
text
}
pub fn detach(&self) {
self.0.borrow_mut().detach();
}
pub fn append_child(&self, new_child: impl Into<IterableNodes>) {
let iter: IterableNodes = new_child.into();
for c in iter.0 {
assert!(*self != c, "a node cannot be appended to itself");
let mut self_borrow = self.0.borrow_mut();
let mut last_child_opt = None;
{
let mut new_child_borrow = c.0.borrow_mut();
new_child_borrow.detach();
new_child_borrow.parent = Some(Rc::downgrade(&self.0));
if let Some(last_child_weak) = self_borrow.last_child.take() {
if let Some(last_child_strong) = last_child_weak.upgrade() {
new_child_borrow.previous_sibling = Some(last_child_weak);
last_child_opt = Some(last_child_strong);
}
}
self_borrow.last_child = Some(Rc::downgrade(&c.0));
}
if let Some(last_child_strong) = last_child_opt {
let mut last_child_borrow = last_child_strong.borrow_mut();
debug_assert!(last_child_borrow.next_sibling.is_none());
last_child_borrow.next_sibling = Some(c.0);
} else {
debug_assert!(self_borrow.first_child.is_none());
self_borrow.first_child = Some(c.0);
}
}
}
pub fn prepend(&self, new_child: DomNode) {
assert!(*self != new_child, "a node cannot be prepended to itself");
let mut self_borrow = self.0.borrow_mut();
{
let mut new_child_borrow = new_child.0.borrow_mut();
new_child_borrow.detach();
new_child_borrow.parent = Some(Rc::downgrade(&self.0));
match self_borrow.first_child.take() {
Some(first_child_strong) => {
{
let mut first_child_borrow = first_child_strong.borrow_mut();
debug_assert!(first_child_borrow.previous_sibling.is_none());
first_child_borrow.previous_sibling = Some(Rc::downgrade(&new_child.0));
}
new_child_borrow.next_sibling = Some(first_child_strong);
}
None => {
debug_assert!(self_borrow.first_child.is_none());
self_borrow.last_child = Some(Rc::downgrade(&new_child.0));
}
}
}
self_borrow.first_child = Some(new_child.0);
}
pub fn insert_after(&self, new_sibling: DomNode) {
assert!(
*self != new_sibling,
"a node cannot be inserted after itself"
);
let mut self_borrow = self.0.borrow_mut();
{
let mut new_sibling_borrow = new_sibling.0.borrow_mut();
new_sibling_borrow.detach();
new_sibling_borrow.parent = self_borrow.parent.clone();
new_sibling_borrow.previous_sibling = Some(Rc::downgrade(&self.0));
match self_borrow.next_sibling.take() {
Some(next_sibling_strong) => {
{
let mut next_sibling_borrow = next_sibling_strong.borrow_mut();
debug_assert!({
let weak = next_sibling_borrow.previous_sibling.as_ref().unwrap();
Rc::ptr_eq(&weak.upgrade().unwrap(), &self.0)
});
next_sibling_borrow.previous_sibling = Some(Rc::downgrade(&new_sibling.0));
}
new_sibling_borrow.next_sibling = Some(next_sibling_strong);
}
None => {
if let Some(parent_ref) = self_borrow.parent.as_ref() {
if let Some(parent_strong) = parent_ref.upgrade() {
let mut parent_borrow = parent_strong.borrow_mut();
parent_borrow.last_child = Some(Rc::downgrade(&new_sibling.0));
}
}
}
}
}
self_borrow.next_sibling = Some(new_sibling.0);
}
pub fn insert_before(&self, new_sibling: DomNode) {
assert!(
*self != new_sibling,
"a node cannot be inserted before itself"
);
let mut self_borrow = self.0.borrow_mut();
let mut previous_sibling_opt = None;
{
let mut new_sibling_borrow = new_sibling.0.borrow_mut();
new_sibling_borrow.detach();
new_sibling_borrow.parent = self_borrow.parent.clone();
new_sibling_borrow.next_sibling = Some(self.0.clone());
if let Some(previous_sibling_weak) = self_borrow.previous_sibling.take() {
if let Some(previous_sibling_strong) = previous_sibling_weak.upgrade() {
new_sibling_borrow.previous_sibling = Some(previous_sibling_weak);
previous_sibling_opt = Some(previous_sibling_strong);
}
}
self_borrow.previous_sibling = Some(Rc::downgrade(&new_sibling.0));
}
if let Some(previous_sibling_strong) = previous_sibling_opt {
let mut previous_sibling_borrow = previous_sibling_strong.borrow_mut();
debug_assert!({
let rc = previous_sibling_borrow.next_sibling.as_ref().unwrap();
Rc::ptr_eq(rc, &self.0)
});
previous_sibling_borrow.next_sibling = Some(new_sibling.0);
} else {
if let Some(parent_ref) = self_borrow.parent.as_ref() {
if let Some(parent_strong) = parent_ref.upgrade() {
let mut parent_borrow = parent_strong.borrow_mut();
parent_borrow.first_child = Some(new_sibling.0);
}
}
}
}
pub fn from_html(html: Html) -> Option<Self> {
match html {
Html::Comment { .. } => None,
Html::Text { text } => Some(DomNode::create_text(text)),
Html::Element {
tag,
attributes,
children,
} => {
let node = DomNode::create_element_with_attributes(tag, attributes);
for c in children {
if let Some(n) = DomNode::from_html(c) {
node.append_child(n);
}
}
Some(node)
}
}
}
}
fn escape_html(text: &str) -> String {
text.chars()
.map(|c| match c {
'&' => "&".to_string(),
'<' => "<".to_string(),
'>' => ">".to_string(),
'"' => """.to_string(),
'\'' => "'".to_string(),
_ => c.to_string(),
})
.collect()
}
impl std::fmt::Display for DomNode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &*self.kind() {
DomNodeKind::Text { text } => write!(f, "{}", escape_html(text)),
DomNodeKind::Element { tag, attributes } => {
let attributes = attributes
.iter()
.map(|(k, v)| {
if !v.is_empty() {
format!(r#"{k}="{v}""#)
} else {
k.into()
}
})
.collect::<Vec<String>>()
.join(" ");
let spacing = if !attributes.is_empty() {
String::from(" ")
} else {
String::new()
};
let children: Vec<DomNode> = self.children().collect();
if children.is_empty() && is_void_element(tag) {
return write!(f, "<{tag}{spacing}{}/>", attributes);
}
let mut content = String::new();
for c in children {
content += &format!("{}", c);
}
write!(f, "<{tag}{spacing}{}>{}</{tag}>", attributes, content)
}
}
}
}
impl Clone for WeakDomNode {
fn clone(&self) -> Self {
WeakDomNode(Weak::clone(&self.0))
}
}
impl fmt::Debug for WeakDomNode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("(WeakNode)")
}
}
impl WeakDomNode {
pub fn upgrade(&self) -> Option<DomNode> {
self.0.upgrade().map(DomNode)
}
}
impl DomNodeData {
fn detach(&mut self) {
let parent_weak = self.parent.take();
let previous_sibling_weak = self.previous_sibling.take();
let next_sibling_strong = self.next_sibling.take();
let previous_sibling_opt = previous_sibling_weak
.as_ref()
.and_then(|weak| weak.upgrade());
if let Some(next_sibling_ref) = next_sibling_strong.as_ref() {
let mut next_sibling_borrow = next_sibling_ref.borrow_mut();
next_sibling_borrow.previous_sibling = previous_sibling_weak;
} else if let Some(parent_ref) = parent_weak.as_ref() {
if let Some(parent_strong) = parent_ref.upgrade() {
let mut parent_borrow = parent_strong.borrow_mut();
parent_borrow.last_child = previous_sibling_weak;
}
}
if let Some(previous_sibling_strong) = previous_sibling_opt {
let mut previous_sibling_borrow = previous_sibling_strong.borrow_mut();
previous_sibling_borrow.next_sibling = next_sibling_strong;
} else if let Some(parent_ref) = parent_weak.as_ref() {
if let Some(parent_strong) = parent_ref.upgrade() {
let mut parent_borrow = parent_strong.borrow_mut();
parent_borrow.first_child = next_sibling_strong;
}
}
}
}
impl Drop for DomNodeData {
fn drop(&mut self) {
let mut stack = Vec::new();
if let Some(first_child) = self.first_child.as_ref() {
let first_child = DomNode(first_child.clone());
for child1 in first_child.following_siblings() {
for child2 in child1.descendants() {
stack.push(child2);
}
}
}
for node in stack {
node.detach();
}
}
}
macro_rules! impl_node_iterator {
($name: ident, $next: expr) => {
impl Iterator for $name {
type Item = DomNode;
fn next(&mut self) -> Option<Self::Item> {
match self.0.take() {
Some(node) => {
self.0 = $next(&node);
Some(node)
}
None => None,
}
}
}
};
}
pub struct Ancestors(Option<DomNode>);
impl_node_iterator!(Ancestors, |node: &DomNode| node.parent());
pub struct PrecedingSiblings(Option<DomNode>);
impl_node_iterator!(PrecedingSiblings, |node: &DomNode| node.previous_sibling());
pub struct FollowingSiblings(Option<DomNode>);
impl_node_iterator!(FollowingSiblings, |node: &DomNode| node.next_sibling());
pub struct Children {
next: Option<DomNode>,
next_back: Option<DomNode>,
}
impl Children {
fn finished(&self) -> bool {
match self.next_back {
Some(ref next_back) => next_back.next_sibling() == self.next,
_ => true,
}
}
}
impl Iterator for Children {
type Item = DomNode;
fn next(&mut self) -> Option<Self::Item> {
if self.finished() {
return None;
}
match self.next.take() {
Some(node) => {
self.next = node.next_sibling();
Some(node)
}
None => None,
}
}
}
impl DoubleEndedIterator for Children {
fn next_back(&mut self) -> Option<Self::Item> {
if self.finished() {
return None;
}
match self.next_back.take() {
Some(node) => {
self.next_back = node.previous_sibling();
Some(node)
}
None => None,
}
}
}
pub struct Descendants(Traverse);
impl Iterator for Descendants {
type Item = DomNode;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.0.next() {
Some(NodeEdge::Start(node)) => return Some(node),
Some(NodeEdge::End(_)) => {}
None => return None,
}
}
}
}
#[derive(Clone, Debug)]
pub enum NodeEdge {
Start(DomNode),
End(DomNode),
}
impl PartialEq for NodeEdge {
fn eq(&self, other: &NodeEdge) -> bool {
match (self, other) {
(NodeEdge::Start(n1), NodeEdge::Start(n2)) => *n1 == *n2,
(NodeEdge::End(n1), NodeEdge::End(n2)) => *n1 == *n2,
_ => false,
}
}
}
impl NodeEdge {
fn next_item(&self, root: &DomNode) -> Option<NodeEdge> {
match *self {
NodeEdge::Start(ref node) => match node.first_child() {
Some(first_child) => Some(NodeEdge::Start(first_child)),
None => Some(NodeEdge::End(node.clone())),
},
NodeEdge::End(ref node) => {
if *node == *root {
None
} else {
match node.next_sibling() {
Some(next_sibling) => Some(NodeEdge::Start(next_sibling)),
None => node.parent().map(NodeEdge::End),
}
}
}
}
}
fn previous_item(&self, root: &DomNode) -> Option<NodeEdge> {
match *self {
NodeEdge::End(ref node) => match node.last_child() {
Some(last_child) => Some(NodeEdge::End(last_child)),
None => Some(NodeEdge::Start(node.clone())),
},
NodeEdge::Start(ref node) => {
if *node == *root {
None
} else {
match node.previous_sibling() {
Some(previous_sibling) => Some(NodeEdge::End(previous_sibling)),
None => node.parent().map(NodeEdge::Start),
}
}
}
}
}
}
pub struct Traverse {
root: DomNode,
next: Option<NodeEdge>,
next_back: Option<NodeEdge>,
}
impl Traverse {
fn finished(&self) -> bool {
match self.next_back {
Some(ref next_back) => next_back.next_item(&self.root) == self.next,
_ => true,
}
}
}
impl Iterator for Traverse {
type Item = NodeEdge;
fn next(&mut self) -> Option<Self::Item> {
if self.finished() {
return None;
}
match self.next.take() {
Some(item) => {
self.next = item.next_item(&self.root);
Some(item)
}
None => None,
}
}
}
impl DoubleEndedIterator for Traverse {
fn next_back(&mut self) -> Option<Self::Item> {
if self.finished() {
return None;
}
match self.next_back.take() {
Some(item) => {
self.next_back = item.previous_item(&self.root);
Some(item)
}
None => None,
}
}
}