virtual_dom/
document.rs

1use std::{collections::HashMap, error::Error};
2
3use crate::Html;
4
5use super::dom_node::DomNode;
6
7#[derive(Debug, Clone)]
8/// RefCell based dom tree, tries to mimick Document as seen in browsers (https://developer.mozilla.org/en-US/docs/Web/API/Document)
9///
10/// using a RC Tree allows for easier manipulation of single nodes and traversing the tree
11pub struct Document {
12    root: DomNode,
13    pub head: DomNode,
14    pub body: DomNode,
15}
16
17impl Document {
18    pub fn from_html(html: Vec<Html>) -> Result<Self, Box<dyn Error>> {
19        let root = html.into_iter().nth(1).ok_or("root not found")?;
20        let root: DomNode = DomNode::from_html(root).ok_or("invalid root html")?;
21        let mut children = root.children();
22        let head = children.next().ok_or("head not found")?;
23        let body = children.next().ok_or("body not found")?;
24
25        Ok(Document { root, head, body })
26    }
27
28    pub fn new() -> Document {
29        let root = DomNode::create_element("html");
30        let head = DomNode::create_element("head");
31        let body = DomNode::create_element("body");
32
33        root.append_child(head.clone());
34        root.append_child(body.clone());
35
36        Document { root, head, body }
37    }
38
39    pub fn root(&self) -> DomNode {
40        self.root.clone()
41    }
42
43    pub fn sanitize(&mut self) {
44        self.root.sanitize_children()
45    }
46
47    pub fn get_elements_by_tag_name(&self, tag: &str) -> Vec<DomNode> {
48        self.root.get_elements_by_tag_name(tag)
49    }
50
51    pub fn get_element_by_id(&self, id: &str) -> Option<DomNode> {
52        self.root
53            .descendants()
54            .find(|e| e.get_attribute("id").map(|a| a == id).unwrap_or(false))
55    }
56
57    pub fn create_element(&self, tag: impl Into<String>) -> DomNode {
58        DomNode::create_element(tag)
59    }
60
61    pub fn create_element_with_attributes(
62        &self,
63        tag: impl Into<String>,
64        attributes: HashMap<String, String>,
65    ) -> DomNode {
66        DomNode::create_element_with_attributes(tag, attributes)
67    }
68
69    pub fn create_text_node(&self, text: impl Into<String>) -> DomNode {
70        DomNode::create_text(text)
71    }
72}
73
74impl std::fmt::Display for Document {
75    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76        write!(f, r#"<!DOCTYPE html>{}"#, self.root)
77    }
78}
79
80impl Default for Document {
81    fn default() -> Self {
82        Self::new()
83    }
84}
85
86/// Utility function to convert iteratables into attributes hashmap
87pub fn to_attributes<I: IntoIterator<Item = (impl Into<String>, impl Into<String>)>>(
88    arr: I,
89) -> HashMap<String, String> {
90    arr.into_iter().map(|(k, v)| (k.into(), v.into())).collect()
91}