1use std::{collections::HashMap, error::Error};
2
3use crate::Html;
4
5use super::dom_node::DomNode;
6
7#[derive(Debug, Clone)]
8pub 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
86pub 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}