parse_html/dom/
dom_tree.rs

1use crate::{
2    lexer::lexer_trait::LexerTrait,
3    node::{ElementNode, Node},
4    parser::{error::ParserError, parser_trait::ParserTrait},
5};
6
7use super::{
8    search_by_attr::{search_all_element_by_attr, search_element_by_attr},
9    search_by_tag_name::{search_all_element_by_tag_name, search_element_by_tag_name},
10};
11
12pub struct DomTree {
13    pub nodes: Vec<Node>,
14}
15
16impl DomTree {
17    pub fn new<L, P>(html: &str) -> Result<Self, ParserError>
18    where
19        L: LexerTrait,
20        P: ParserTrait,
21    {
22        let mut lexer = L::new(html);
23        let tokens = lexer.tokenize();
24
25        let mut parser = P::new(tokens);
26        let nodes = parser.parse()?;
27
28        Ok(Self { nodes })
29    }
30
31    pub fn get_by_id(&self, id_value: &str) -> Option<&ElementNode> {
32        for node in &self.nodes {
33            if let Some(found) = search_element_by_attr(node, "id", id_value) {
34                return Some(found);
35            }
36        }
37        None
38    }
39
40    pub fn get_all_by_id(&self, id_value: &str) -> Vec<&ElementNode> {
41        let mut list_found = vec![];
42        for node in &self.nodes {
43            list_found.extend(search_all_element_by_attr(node, "id", id_value));
44        }
45        list_found
46    }
47
48    pub fn get_by_class(&self, value: &str) -> Option<&ElementNode> {
49        for node in &self.nodes {
50            if let Some(found) = search_element_by_attr(node, "class", value) {
51                return Some(found);
52            }
53        }
54        None
55    }
56
57    pub fn get_all_by_class(&self, value: &str) -> Vec<&ElementNode> {
58        let mut list_found = vec![];
59        for node in &self.nodes {
60            list_found.extend(search_all_element_by_attr(node, "class", value));
61        }
62        list_found
63    }
64
65    pub fn get_by_tag_name(&self, tag_name: &str) -> Option<&ElementNode> {
66        for node in &self.nodes {
67            if let Some(found) = search_element_by_tag_name(node, tag_name) {
68                return Some(found);
69            }
70        }
71        None
72    }
73
74    pub fn get_all_by_tag_name(&self, tag_name: &str) -> Vec<&ElementNode> {
75        let mut list_found = vec![];
76        for node in &self.nodes {
77            list_found.extend(search_all_element_by_tag_name(node, tag_name));
78        }
79        list_found
80    }
81
82    pub fn get_by_attr(
83        &self,
84        attributes_name: &str,
85        attributes_value: &str,
86    ) -> Option<&ElementNode> {
87        for node in &self.nodes {
88            if let Some(found) = search_element_by_attr(node, attributes_name, attributes_value) {
89                return Some(found);
90            }
91        }
92        None
93    }
94
95    pub fn get_all_by_attr(
96        &self,
97        attributes_name: &str,
98        attributes_value: &str,
99    ) -> Vec<&ElementNode> {
100        let mut list_found = vec![];
101        for node in &self.nodes {
102            list_found.extend(search_all_element_by_attr(
103                node,
104                attributes_name,
105                attributes_value,
106            ));
107        }
108        list_found
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115
116    #[test]
117    fn test_chainning() {
118        let grandchild_node = ElementNode {
119            tag_name: "div".to_string(),
120            attributes: vec![("id".to_string(), "grandchild_node".to_string())],
121            children: vec![],
122        };
123
124        let child_node = ElementNode {
125            tag_name: "div".to_string(),
126            attributes: vec![
127                ("id".to_string(), "child".to_string()),
128                ("class".to_string(), "space".to_string()),
129            ],
130            children: vec![Node::Element(grandchild_node)],
131        };
132
133        let button = ElementNode {
134            tag_name: "button".to_string(),
135            attributes: vec![("class".to_string(), "btn".to_string())],
136            children: vec![Node::Text("envoyer".to_string())],
137        };
138
139        let parent_node = ElementNode {
140            tag_name: "div".to_string(),
141            attributes: vec![("class".to_string(), "mt-1".to_string())],
142            children: vec![Node::Element(child_node), Node::Element(button)],
143        };
144
145        let container = ElementNode {
146            tag_name: "div".to_string(),
147            attributes: vec![("class".to_string(), "container".to_string())],
148            children: vec![Node::Element(parent_node)],
149        };
150
151        let found_parent = container.get_by_class("mt-1");
152        let parent_element = found_parent.unwrap();
153        let found_child = parent_element.get_by_class("space");
154        let child_element = found_child.unwrap();
155        let found_grandchild = child_element.get_by_id("grandchild_node");
156        let found_child_not_exist = child_element.get_by_id("adfaf");
157        let found_button = container.get_by_tag_name("button");
158        let result_all_div_from_container = container.get_all_by_tag_name("div");
159        let result_all_button = container.get_all_by_tag_name("button");
160        let result_all_span = container.get_all_by_tag_name("span");
161
162        assert!(found_grandchild.is_some());
163        assert!(found_child_not_exist.is_none());
164        assert!(found_button.is_some());
165        assert!(result_all_div_from_container.len() == 3);
166        assert!(result_all_button.len() == 1);
167        assert!(result_all_span.len() == 0);
168    }
169}