use crate::models::namespace::Namespace;
use crate::models::token::{Attribute, Span};
pub(crate) type NodeId = usize;
#[derive(Debug, Default)]
pub(crate) struct Dom {
pub(crate) nodes: Vec<DomNode>,
}
#[derive(Debug)]
pub(crate) struct DomNode {
pub(crate) data: NodeData,
pub(crate) parent: Option<NodeId>,
pub(crate) first_child: Option<NodeId>,
pub(crate) last_child: Option<NodeId>,
pub(crate) previous: Option<NodeId>,
pub(crate) next: Option<NodeId>,
pub(crate) location: Option<Location>,
}
#[derive(Debug)]
pub(crate) enum NodeData {
Document { quirks: DocumentMode },
Fragment,
Element(Element),
Text(String),
Comment(String),
Doctype,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum DocumentMode {
NoQuirks,
Quirks,
LimitedQuirks,
}
#[derive(Debug)]
pub(crate) struct Element {
pub(crate) tag_name: String,
pub(crate) namespace: Namespace,
pub(crate) attrs: Vec<Attribute>,
pub(crate) template_content: Option<NodeId>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct Location {
pub(crate) start: usize,
pub(crate) end: usize,
pub(crate) start_tag: Option<Span>,
pub(crate) end_tag: Option<Span>,
}
impl Location {
pub(crate) fn of(span: Span) -> Location {
Location {
start: span.start,
end: span.end,
start_tag: None,
end_tag: None,
}
}
}
impl Dom {
pub(crate) fn create(&mut self, data: NodeData) -> NodeId {
self.nodes.push(DomNode {
data,
parent: None,
first_child: None,
last_child: None,
previous: None,
next: None,
location: None,
});
self.nodes.len() - 1
}
pub(crate) fn create_element(
&mut self,
tag_name: &str,
namespace: Namespace,
attrs: Vec<Attribute>,
) -> NodeId {
self.create(NodeData::Element(Element {
tag_name: tag_name.to_string(),
namespace,
attrs,
template_content: None,
}))
}
pub(crate) fn element(&self, node: NodeId) -> Option<&Element> {
match &self.nodes[node].data {
NodeData::Element(element) => Some(element),
_ => None,
}
}
pub(crate) fn element_mut(&mut self, node: NodeId) -> Option<&mut Element> {
match &mut self.nodes[node].data {
NodeData::Element(element) => Some(element),
_ => None,
}
}
pub(crate) fn namespace(&self, node: NodeId) -> Namespace {
self.element(node)
.map_or(Namespace::Html, |element| element.namespace)
}
pub(crate) fn tag_name(&self, node: NodeId) -> &str {
self.element(node)
.map_or("", |element| element.tag_name.as_str())
}
pub(crate) fn template_content(&self, node: NodeId) -> Option<NodeId> {
self.element(node)
.and_then(|element| element.template_content)
}
pub(crate) fn append_child(&mut self, parent: NodeId, child: NodeId) {
let last = self.nodes[parent].last_child;
self.nodes[child].parent = Some(parent);
self.nodes[child].previous = last;
self.nodes[child].next = None;
match last {
Some(last) => self.nodes[last].next = Some(child),
None => self.nodes[parent].first_child = Some(child),
}
self.nodes[parent].last_child = Some(child);
}
pub(crate) fn insert_before(
&mut self,
parent: NodeId,
child: NodeId,
reference: NodeId,
) {
let previous = self.nodes[reference].previous;
self.nodes[child].parent = Some(parent);
self.nodes[child].previous = previous;
self.nodes[child].next = Some(reference);
self.nodes[reference].previous = Some(child);
match previous {
Some(previous) => self.nodes[previous].next = Some(child),
None => self.nodes[parent].first_child = Some(child),
}
}
pub(crate) fn detach(&mut self, node: NodeId) {
let Some(parent) = self.nodes[node].parent else {
return;
};
let previous = self.nodes[node].previous;
let next = self.nodes[node].next;
match previous {
Some(previous) => self.nodes[previous].next = next,
None => self.nodes[parent].first_child = next,
}
match next {
Some(next) => self.nodes[next].previous = previous,
None => self.nodes[parent].last_child = previous,
}
self.nodes[node].parent = None;
self.nodes[node].previous = None;
self.nodes[node].next = None;
}
pub(crate) fn insert_text(
&mut self,
parent: NodeId,
text: &str,
before: Option<NodeId>,
) -> NodeId {
let previous = match before {
Some(before) => self.nodes[before].previous,
None => self.nodes[parent].last_child,
};
if let Some(previous) = previous
&& let NodeData::Text(value) = &mut self.nodes[previous].data
{
value.push_str(text);
return previous;
}
let node = self.create(NodeData::Text(text.to_string()));
match before {
Some(before) => self.insert_before(parent, node, before),
None => self.append_child(parent, node),
}
node
}
pub(crate) fn adopt_children(&mut self, donor: NodeId, recipient: NodeId) {
while let Some(child) = self.nodes[donor].first_child {
self.detach(child);
self.append_child(recipient, child);
}
}
pub(crate) fn children(&self, node: NodeId) -> Children<'_> {
Children {
dom: self,
next: self.nodes[node].first_child,
}
}
}
pub(crate) struct Children<'d> {
dom: &'d Dom,
next: Option<NodeId>,
}
impl Iterator for Children<'_> {
type Item = NodeId;
fn next(&mut self) -> Option<NodeId> {
let current = self.next?;
self.next = self.dom.nodes[current].next;
Some(current)
}
}