#[derive(Debug, Clone, PartialEq)]
pub enum NodeType {
Document,
Element(ElementData),
Text(String),
Comment(String),
}
#[derive(Debug, Clone, PartialEq)]
pub struct ElementData {
pub tag: String,
pub attributes: Vec<(String, String)>,
pub id: Option<String>,
pub classes: Vec<String>,
pub is_hovered: bool,
pub is_focused: bool,
pub is_active: bool,
}
impl ElementData {
pub fn new(tag: String, attributes: Vec<(String, String)>) -> Self {
let id = attributes
.iter()
.find(|(k, _)| k == "id")
.map(|(_, v)| v.clone());
let classes = attributes
.iter()
.find(|(k, _)| k == "class")
.map(|(_, v)| {
v.split_whitespace()
.map(|s| s.to_string())
.collect::<Vec<_>>()
})
.unwrap_or_default();
ElementData {
tag,
attributes,
id,
classes,
is_hovered: false,
is_focused: false,
is_active: false,
}
}
pub fn get_attribute(&self, name: &str) -> Option<&str> {
self.attributes
.iter()
.find(|(k, _)| k.eq_ignore_ascii_case(name))
.map(|(_, v)| v.as_str())
}
pub fn has_attribute(&self, name: &str) -> bool {
self.attributes
.iter()
.any(|(k, _)| k.eq_ignore_ascii_case(name))
}
pub fn has_class(&self, class: &str) -> bool {
self.classes.iter().any(|c| c == class)
}
pub fn set_hovered(&mut self, hovered: bool) {
self.is_hovered = hovered;
}
pub fn set_focused(&mut self, focused: bool) {
self.is_focused = focused;
}
pub fn set_active(&mut self, active: bool) {
self.is_active = active;
}
}
#[derive(Debug, Clone)]
pub struct DomNode {
pub node_type: NodeType,
pub parent: Option<usize>,
pub children: Vec<usize>,
}
#[derive(Debug, Clone)]
pub struct Dom {
pub nodes: Vec<DomNode>,
pub root: usize,
}
impl Dom {
pub fn element_at(&self, idx: usize) -> Option<&ElementData> {
match &self.nodes.get(idx)?.node_type {
NodeType::Element(e) => Some(e),
_ => None,
}
}
pub fn element_at_mut(&mut self, idx: usize) -> Option<&mut ElementData> {
match &mut self.nodes.get_mut(idx)?.node_type {
NodeType::Element(e) => Some(e),
_ => None,
}
}
pub fn set_hovered(&mut self, idx: usize, hovered: bool) {
if let Some(elem) = self.element_at_mut(idx) {
elem.set_hovered(hovered);
}
}
pub fn set_focused(&mut self, idx: usize, focused: bool) {
if let Some(elem) = self.element_at_mut(idx) {
elem.set_focused(focused);
}
}
pub fn set_active(&mut self, idx: usize, active: bool) {
if let Some(elem) = self.element_at_mut(idx) {
elem.set_active(active);
}
}
pub fn parent(&self, idx: usize) -> Option<usize> {
self.nodes.get(idx)?.parent
}
pub fn children(&self, idx: usize) -> &[usize] {
self.nodes
.get(idx)
.map(|n| n.children.as_slice())
.unwrap_or(&[])
}
pub fn first_child(&self, idx: usize) -> Option<usize> {
self.nodes.get(idx)?.children.first().copied()
}
pub fn last_child(&self, idx: usize) -> Option<usize> {
self.nodes.get(idx)?.children.last().copied()
}
pub fn preceding_sibling(&self, idx: usize) -> Option<usize> {
let parent = self.nodes.get(idx)?.parent?;
let siblings = &self.nodes.get(parent)?.children;
let pos = siblings.iter().position(|&n| n == idx)?;
if pos == 0 {
None
} else {
Some(siblings[pos - 1])
}
}
pub fn next_sibling(&self, idx: usize) -> Option<usize> {
let parent = self.nodes.get(idx)?.parent?;
let siblings = &self.nodes.get(parent)?.children;
let pos = siblings.iter().position(|&n| n == idx)?;
siblings.get(pos + 1).copied()
}
pub fn preceding_siblings(&self, idx: usize) -> Vec<usize> {
match self.nodes.get(idx).and_then(|n| n.parent) {
Some(parent) => {
if let Some(parent_node) = self.nodes.get(parent) {
let siblings = &parent_node.children;
match siblings.iter().position(|&n| n == idx) {
Some(pos) => siblings[..pos].to_vec(),
None => vec![],
}
} else {
vec![]
}
}
None => vec![],
}
}
pub fn ancestors(&self, idx: usize) -> Vec<usize> {
let mut list = Vec::new();
let mut curr = self.nodes.get(idx).and_then(|n| n.parent);
while let Some(p) = curr {
list.push(p);
curr = self.nodes.get(p).and_then(|n| n.parent);
}
list
}
pub fn descendants(&self, idx: usize) -> Vec<usize> {
let mut result = Vec::new();
self.collect_descendants(idx, &mut result);
result
}
fn collect_descendants(&self, idx: usize, result: &mut Vec<usize>) {
if let Some(node) = self.nodes.get(idx) {
for &child in &node.children {
result.push(child);
self.collect_descendants(child, result);
}
}
}
pub fn get_element_by_id(&self, id: &str) -> Option<usize> {
self.nodes.iter().enumerate().find_map(|(idx, node)| {
if let NodeType::Element(ref elem) = node.node_type {
if elem.id.as_deref() == Some(id) {
return Some(idx);
}
}
None
})
}
pub fn get_elements_by_tag_name(&self, tag: &str) -> Vec<usize> {
let is_wildcard = tag == "*";
self.nodes
.iter()
.enumerate()
.filter_map(|(idx, node)| {
if let NodeType::Element(ref elem) = node.node_type {
if is_wildcard || elem.tag.eq_ignore_ascii_case(tag) {
return Some(idx);
}
}
None
})
.collect()
}
pub fn get_elements_by_class_name(&self, class: &str) -> Vec<usize> {
self.nodes
.iter()
.enumerate()
.filter_map(|(idx, node)| {
if let NodeType::Element(ref elem) = node.node_type {
if elem.has_class(class) {
return Some(idx);
}
}
None
})
.collect()
}
pub fn text_content(&self, idx: usize) -> String {
let mut text = String::new();
self.collect_text(idx, &mut text);
text
}
fn collect_text(&self, idx: usize, out: &mut String) {
if let Some(node) = self.nodes.get(idx) {
match &node.node_type {
NodeType::Text(t) => {
out.push_str(t);
}
_ => {
for &child in &node.children {
self.collect_text(child, out);
}
}
}
}
}
pub fn query_selector(&self, selector_str: &str) -> Option<usize> {
let selector = crate::css::parser::parse_selector(selector_str)?;
let mut candidate_order = vec![self.root];
candidate_order.extend(self.descendants(self.root));
candidate_order
.into_iter()
.find(|&idx| crate::style::matching::matches_selector(self, idx, &selector))
}
pub fn query_selector_all(&self, selector_str: &str) -> Vec<usize> {
let selector = match crate::css::parser::parse_selector(selector_str) {
Some(s) => s,
None => return Vec::new(),
};
let mut candidate_order = vec![self.root];
candidate_order.extend(self.descendants(self.root));
candidate_order
.into_iter()
.filter(|&idx| crate::style::matching::matches_selector(self, idx, &selector))
.collect()
}
}