1use crate::node::{ElementNode, Node};
2
3pub fn search_node_attr<'a>(
4 node: &'a Node,
5 attributes_name: &str,
6 attributes_value: &str,
7) -> Option<&'a ElementNode> {
8 if let Node::Element(element) = node {
9 for (attr_name, attr_value) in &element.attributes {
10 if attr_name == attributes_name && attributes_value == attr_value {
11 return Some(element);
12 }
13 }
14 for child in &element.children {
15 if let Some(found) = search_node_attr(child, attributes_name, attributes_value) {
16 return Some(found);
17 }
18 }
19 }
20 None
21}
22
23#[cfg(test)]
24mod tests {
25 use super::*;
26
27 #[test]
28 fn should_matching_id() {
29 let child_node = ElementNode {
30 tag_name: "div".to_string(),
31 attributes: vec![("id".to_string(), "child".to_string())],
32 children: vec![],
33 };
34
35 let parent_node = ElementNode {
36 tag_name: "div".to_string(),
37 attributes: vec![],
38 children: vec![Node::Element(child_node)],
39 };
40
41 let node = Node::Element(parent_node);
42 let result = search_node_attr(&node, "id", "child");
43 assert!(result.is_some());
44 assert_eq!(result.unwrap().tag_name, "div");
45 }
46
47 #[test]
48 fn should_no_matching_id() {
49 let child_node = ElementNode {
50 tag_name: "div".to_string(),
51 attributes: vec![("id".to_string(), "child".to_string())],
52 children: vec![],
53 };
54
55 let parent_node = ElementNode {
56 tag_name: "div".to_string(),
57 attributes: vec![],
58 children: vec![Node::Element(child_node)],
59 };
60
61 let node = Node::Element(parent_node);
62 let result = search_node_attr(&node, "id", "dasfdsa");
63 assert!(result.is_none());
64 }
65
66 #[test]
67 fn should_grandchild_matching_id() {
68 let grandchild_node = ElementNode {
69 tag_name: "span".to_string(),
70 attributes: vec![("id".to_string(), "grandchild".to_string())],
71 children: vec![],
72 };
73
74 let child_node = ElementNode {
75 tag_name: "div".to_string(),
76 attributes: vec![("id".to_string(), "child".to_string())],
77 children: vec![Node::Element(grandchild_node)],
78 };
79
80 let parent_node = ElementNode {
81 tag_name: "div".to_string(),
82 attributes: vec![],
83 children: vec![Node::Element(child_node)],
84 };
85
86 let node = Node::Element(parent_node);
87 let result = search_node_attr(&node, "id", "grandchild");
88 assert!(result.is_some());
89 assert_eq!(result.unwrap().tag_name, "span");
90 }
91
92 #[test]
93 fn should_matching_class() {
94 let child_node = ElementNode {
95 tag_name: "div".to_string(),
96 attributes: vec![("class".to_string(), "child".to_string())],
97 children: vec![],
98 };
99
100 let parent_node = ElementNode {
101 tag_name: "div".to_string(),
102 attributes: vec![],
103 children: vec![Node::Element(child_node)],
104 };
105
106 let node = Node::Element(parent_node);
107 let result = search_node_attr(&node, "class", "child");
108 assert!(result.is_some());
109 assert_eq!(result.unwrap().tag_name, "div");
110 }
111
112 #[test]
113 fn should_no_matching_class() {
114 let child_node = ElementNode {
115 tag_name: "div".to_string(),
116 attributes: vec![("class".to_string(), "child".to_string())],
117 children: vec![],
118 };
119
120 let parent_node = ElementNode {
121 tag_name: "div".to_string(),
122 attributes: vec![],
123 children: vec![Node::Element(child_node)],
124 };
125
126 let node = Node::Element(parent_node);
127 let result = search_node_attr(&node, "class", "dasfdsa");
128 assert!(result.is_none());
129 }
130}