1use crate::ast::{
2 AttributeSelector, Combinator, CompoundSelector, QueryAst, SelectorSequence, SimpleSelector,
3};
4
5pub trait QueryTreeAdapter {
6 type NodeId: Copy + Eq;
7
8 fn children_of(&self, node: Self::NodeId) -> Vec<Self::NodeId>;
9
10 fn matches_type(&self, _node: Self::NodeId, _type_name: &str) -> bool {
11 false
12 }
13
14 fn matches_id(&self, _node: Self::NodeId, _id: &str) -> bool {
15 false
16 }
17
18 fn matches_guid(&self, _node: Self::NodeId, _guid: &str) -> bool {
19 false
20 }
21
22 fn matches_name(&self, _node: Self::NodeId, _name: &str) -> bool {
23 false
24 }
25
26 fn matches_class(&self, _node: Self::NodeId, _class_name: &str) -> bool {
27 false
28 }
29
30 fn matches_attribute(&self, _node: Self::NodeId, attribute: &AttributeSelector) -> bool {
31 if attribute.name == "name" {
32 return attribute
33 .value
34 .as_deref()
35 .map(|name| self.matches_name(_node, name))
36 .unwrap_or(false);
37 }
38 false
39 }
40}
41
42#[derive(Debug, Default)]
43pub struct QueryEvaluator;
44
45impl QueryEvaluator {
46 pub fn evaluate<T: QueryTreeAdapter>(
49 tree: &T,
50 root: T::NodeId,
51 ast: &QueryAst,
52 ) -> Vec<T::NodeId> {
53 let mut out = Vec::new();
54 for sequence in &ast.selector_groups {
55 let mut path: Vec<T::NodeId> = Vec::new();
56 collect(tree, root, sequence, &mut path, &mut out);
57 }
58 out
59 }
60
61 fn matches_compound<T: QueryTreeAdapter>(
62 tree: &T,
63 node: T::NodeId,
64 compound: &CompoundSelector,
65 ) -> bool {
66 compound
67 .simple_selectors
68 .iter()
69 .all(|selector| Self::matches_simple(tree, node, selector))
70 }
71
72 fn matches_simple<T: QueryTreeAdapter>(
73 tree: &T,
74 node: T::NodeId,
75 selector: &SimpleSelector,
76 ) -> bool {
77 match selector {
78 SimpleSelector::Universal => true,
79 SimpleSelector::Type(type_name) => tree.matches_type(node, type_name),
80 SimpleSelector::Id(id) => tree.matches_id(node, id),
81 SimpleSelector::Guid(guid) => tree.matches_guid(node, guid),
82 SimpleSelector::Name(name) => tree.matches_name(node, name),
83 SimpleSelector::Class(class_name) => tree.matches_class(node, class_name),
84 SimpleSelector::Attribute(attribute) => tree.matches_attribute(node, attribute),
85 }
86 }
87}
88
89fn collect<T: QueryTreeAdapter>(
90 tree: &T,
91 node: T::NodeId,
92 sequence: &SelectorSequence,
93 path: &mut Vec<T::NodeId>,
94 out: &mut Vec<T::NodeId>,
95) {
96 path.push(node);
97 if matches_with_path::<T>(tree, sequence, path) {
98 out.push(node);
99 }
100 for child in tree.children_of(node) {
101 collect(tree, child, sequence, path, out);
102 }
103 path.pop();
104}
105
106fn matches_with_path<T: QueryTreeAdapter>(
109 tree: &T,
110 sequence: &SelectorSequence,
111 path: &[T::NodeId],
112) -> bool {
113 let segs = &sequence.segments;
114 if segs.is_empty() || path.is_empty() {
115 return false;
116 }
117 let last = segs.len() - 1;
118 let candidate = path[path.len() - 1];
119 if !QueryEvaluator::matches_compound(tree, candidate, &segs[last].compound) {
120 return false;
121 }
122
123 let mut path_cursor: isize = path.len() as isize - 1;
126 for i in (0..last).rev() {
127 let combinator = segs[i + 1].combinator.unwrap_or(Combinator::Descendant);
128 match combinator {
129 Combinator::Child => {
130 path_cursor -= 1;
131 if path_cursor < 0 {
132 return false;
133 }
134 if !QueryEvaluator::matches_compound(
135 tree,
136 path[path_cursor as usize],
137 &segs[i].compound,
138 ) {
139 return false;
140 }
141 }
142 Combinator::Descendant => {
143 path_cursor -= 1;
144 let mut found = false;
145 while path_cursor >= 0 {
146 if QueryEvaluator::matches_compound(
147 tree,
148 path[path_cursor as usize],
149 &segs[i].compound,
150 ) {
151 found = true;
152 break;
153 }
154 path_cursor -= 1;
155 }
156 if !found {
157 return false;
158 }
159 }
160 }
161 }
162 true
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use crate::ast::{CompoundSelector, SelectorSegment, SelectorSequence, SimpleSelector};
169
170 struct ToyTree {
173 nodes: Vec<(Option<usize>, &'static str, &'static str)>,
174 }
175
176 impl QueryTreeAdapter for ToyTree {
177 type NodeId = usize;
178 fn children_of(&self, node: usize) -> Vec<usize> {
179 self.nodes
180 .iter()
181 .enumerate()
182 .filter(|(_, (p, _, _))| *p == Some(node))
183 .map(|(i, _)| i)
184 .collect()
185 }
186 fn matches_type(&self, node: usize, type_name: &str) -> bool {
187 self.nodes[node].1 == type_name
188 }
189 fn matches_name(&self, node: usize, name: &str) -> bool {
190 self.nodes[node].2 == name
191 }
192 }
193
194 fn type_seg(t: &'static str, comb: Option<Combinator>) -> SelectorSegment {
195 SelectorSegment {
196 combinator: comb,
197 compound: CompoundSelector {
198 simple_selectors: vec![SimpleSelector::Type(t.into())],
199 },
200 }
201 }
202
203 fn build_tree() -> ToyTree {
211 ToyTree {
212 nodes: vec![
213 (None, "Root", "root"),
214 (Some(0), "A", "a"),
215 (Some(1), "B", "b"),
216 (Some(2), "C", "c"),
217 (Some(0), "A", "a2"),
218 (Some(4), "C", "c2"),
219 ],
220 }
221 }
222
223 #[test]
224 fn child_combinator_filters_indirect_descendants() {
225 let tree = build_tree();
226 let ast = QueryAst {
227 selector_groups: vec![SelectorSequence {
228 segments: vec![type_seg("A", None), type_seg("C", Some(Combinator::Child))],
229 }],
230 };
231 let matches = QueryEvaluator::evaluate(&tree, 0, &ast);
232 assert_eq!(matches, vec![5]);
235 }
236
237 #[test]
238 fn descendant_combinator_matches_at_any_depth() {
239 let tree = build_tree();
240 let ast = QueryAst {
241 selector_groups: vec![SelectorSequence {
242 segments: vec![
243 type_seg("A", None),
244 type_seg("C", Some(Combinator::Descendant)),
245 ],
246 }],
247 };
248 let mut matches = QueryEvaluator::evaluate(&tree, 0, &ast);
249 matches.sort();
250 assert_eq!(matches, vec![3, 5]);
251 }
252
253 #[test]
254 fn single_segment_matches_all_descendants() {
255 let tree = build_tree();
256 let ast = QueryAst {
257 selector_groups: vec![SelectorSequence {
258 segments: vec![type_seg("A", None)],
259 }],
260 };
261 let mut matches = QueryEvaluator::evaluate(&tree, 0, &ast);
262 matches.sort();
263 assert_eq!(matches, vec![1, 4]);
264 }
265}