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::{search_by_attr::search_node_attr, search_by_tag_name::search_node_by_tag_name};
8
9pub struct DomTree {
10    pub nodes: Vec<Node>,
11}
12
13impl DomTree {
14    pub fn new<L, P>(html: &str) -> Result<Self, ParserError>
15    where
16        L: LexerTrait,
17        P: ParserTrait,
18    {
19        let mut lexer = L::new(html);
20        let tokens = lexer.tokenize();
21
22        let mut parser = P::new(tokens);
23        let nodes = parser.parse()?;
24
25        Ok(Self { nodes })
26    }
27
28    pub fn get_by_id(&self, id_value: &str) -> Option<&ElementNode> {
29        for node in &self.nodes {
30            if let Some(found) = search_node_attr(node, "id", id_value) {
31                return Some(found);
32            }
33        }
34        None
35    }
36
37    pub fn get_by_class(&self, value: &str) -> Option<&ElementNode> {
38        for node in &self.nodes {
39            if let Some(found) = search_node_attr(node, "class", value) {
40                return Some(found);
41            }
42        }
43        None
44    }
45
46    pub fn get_by_tag_name(&self, tag_name: &str) -> Option<&ElementNode> {
47        for node in &self.nodes {
48            if let Some(found) = search_node_by_tag_name(node, tag_name) {
49                return Some(found);
50            }
51        }
52        None
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn test_chainning() {
62        let grandchild_node = ElementNode {
63            tag_name: "div".to_string(),
64            attributes: vec![("id".to_string(), "grandchild_node".to_string())],
65            children: vec![],
66        };
67
68        let child_node = ElementNode {
69            tag_name: "div".to_string(),
70            attributes: vec![
71                ("id".to_string(), "child".to_string()),
72                ("class".to_string(), "space".to_string()),
73            ],
74            children: vec![Node::Element(grandchild_node)],
75        };
76
77        let button = ElementNode {
78            tag_name: "button".to_string(),
79            attributes: vec![("class".to_string(), "btn".to_string())],
80            children: vec![Node::Text("envoyer".to_string())],
81        };
82
83        let parent_node = ElementNode {
84            tag_name: "div".to_string(),
85            attributes: vec![("class".to_string(), "mt-1".to_string())],
86            children: vec![Node::Element(child_node), Node::Element(button)],
87        };
88
89        let container = ElementNode {
90            tag_name: "div".to_string(),
91            attributes: vec![("class".to_string(), "container".to_string())],
92            children: vec![Node::Element(parent_node)],
93        };
94
95        let found_parent = container.get_by_class("mt-1");
96        let parent_element = found_parent.unwrap();
97        let found_child = parent_element.get_by_class("space");
98        let child_element = found_child.unwrap();
99        let found_grandchild = child_element.get_by_id("grandchild_node");
100        let found_child_not_exist = child_element.get_by_id("adfaf");
101        let found_button = container.get_by_tag_name("button");
102        assert!(found_grandchild.is_some());
103        assert!(found_child_not_exist.is_none());
104        assert!(found_button.is_some());
105    }
106}