node_html_parser/css_select/
general.rs1use crate::css_select::attributes::compose_attribute;
2use crate::css_select::legacy::NthExpr;
3use crate::css_select::types::{Adapter, InternalSelector, PseudoData};
4
5pub fn compile_general_selector<'a, A: Adapter + 'a>(
6 next: Box<dyn Fn(&A::HTMLElement) -> bool + 'a>,
7 selector: &InternalSelector,
8 adapter: &'a A,
9) -> Box<dyn Fn(&A::HTMLElement) -> bool + 'a> {
10 match selector {
11 InternalSelector::Universal => next,
12 InternalSelector::Tag { name } => {
13 let name = name.clone();
14 Box::new(move |el| adapter.get_name(el) == name && next(el))
15 }
16 InternalSelector::Attribute(sel) => compose_attribute(next, sel, adapter),
17 InternalSelector::Descendant => Box::new(move |el| {
18 let mut current = adapter.get_parent(el);
19 while let Some(p) = current {
20 if next(p) {
21 return true;
22 }
23 current = adapter.get_parent(p);
24 }
25 false
26 }),
27 InternalSelector::Child => {
28 Box::new(move |el| adapter.get_parent(el).map(|p| next(p)).unwrap_or(false))
29 }
30 InternalSelector::Sibling => Box::new(move |el| {
31 let sibs = adapter.get_siblings(el);
32 for s in sibs {
33 if adapter.equals(s, el) {
34 break;
35 }
36 if next(s) {
37 return true;
38 }
39 }
40 false
41 }),
42 InternalSelector::Adjacent => Box::new(move |el| {
43 let sibs = adapter.get_siblings(el);
44 let mut last: Option<&A::HTMLElement> = None;
45 for s in sibs {
46 if adapter.equals(s, el) {
47 break;
48 }
49 last = Some(s);
50 }
51 last.map(|l| next(l)).unwrap_or(false)
52 }),
53 InternalSelector::FlexibleDescendant => Box::new(move |el| {
54 let mut cur: Option<&A::HTMLElement> = Some(el);
55 while let Some(c) = cur {
56 if next(c) {
57 return true;
58 }
59 cur = adapter.get_parent(c);
60 }
61 false
62 }),
63 InternalSelector::Pseudo { name, data } => {
64 let n = name.clone();
66 let d = data.clone();
67 let compiled_sub: Option<Vec<Box<dyn Fn(&A::HTMLElement) -> bool + 'a>>> = match &d {
68 PseudoData::SubSelectors(groups) => {
69 let mut list = Vec::with_capacity(groups.len());
70 for seq in groups {
71 let mut inner: Box<dyn Fn(&A::HTMLElement) -> bool + 'a> =
73 Box::new(|_| true);
74 let mut seq_clone = seq.clone();
76 crate::css_select::helpers::selectors::sort_rules(&mut seq_clone);
77 for tok in &seq_clone {
78 inner = compile_general_selector(inner, tok, adapter);
79 }
80 list.push(inner);
81 }
82 Some(list)
83 }
84 _ => None,
85 };
86 Box::new(move |el| match n.as_str() {
87 "scope" => next(el),
88 "empty" => {
89 if adapter.is_tag(el) && adapter.is_empty(el) {
90 return next(el);
91 }
92 false
93 }
94 "first-child" => is_first_child(el, adapter) && next(el),
95 "last-child" => is_last_child(el, adapter) && next(el),
96 "only-child" => is_only_child(el, adapter) && next(el),
97 "first-of-type" => is_first_of_type(el, adapter) && next(el),
98 "last-of-type" => is_last_of_type(el, adapter) && next(el),
99 "only-of-type" => is_only_of_type(el, adapter) && next(el),
100 "root" => adapter.get_parent(el).is_none() && next(el),
101 "not" => {
102 if let Some(subs) = &compiled_sub {
103 for f in subs {
104 if f(el) {
105 return false;
106 }
107 }
108 return next(el);
109 }
110 next(el)
111 }
112 "is" | "where" => {
113 if let Some(subs) = &compiled_sub {
114 for (_idx, f) in subs.iter().enumerate() {
115 let matched = f(el);
116 if matched {
117 return next(el);
118 }
119 }
120 return false;
121 }
122 false
123 }
124 "has" => {
125 if let Some(subs) = &compiled_sub {
126 if has_descendant_with_any(el, adapter, subs) {
127 return next(el);
128 }
129 return false;
130 }
131 false
132 }
133 "nth-child" | "nth-last-child" | "nth-of-type" | "nth-last-of-type" => match &d {
134 PseudoData::Nth(expr) => {
135 if match_nth_pseudo(el, adapter, n.as_str(), expr) {
136 return next(el);
137 }
138 false
139 }
140 _ => false,
141 },
142 _ => false,
143 })
144 }
145 }
146}
147
148fn is_first_child<A: Adapter>(el: &A::HTMLElement, adapter: &A) -> bool {
150 let siblings = adapter.get_siblings(el);
151 for s in siblings {
152 if adapter.is_tag(s) {
153 return adapter.equals(s, el);
154 }
155 }
156 false
157}
158fn is_last_child<A: Adapter>(el: &A::HTMLElement, adapter: &A) -> bool {
159 let siblings = adapter.get_siblings(el);
160 for s in siblings.iter().rev() {
161 if adapter.is_tag(s) {
162 return adapter.equals(s, el);
163 }
164 }
165 false
166}
167fn is_only_child<A: Adapter>(el: &A::HTMLElement, adapter: &A) -> bool {
168 let siblings = adapter.get_siblings(el);
169 let mut found_other = false;
170 for s in siblings {
171 if adapter.is_tag(s) && !adapter.equals(s, el) {
172 found_other = true;
173 break;
174 }
175 }
176 !found_other
177}
178fn is_first_of_type<A: Adapter>(el: &A::HTMLElement, adapter: &A) -> bool {
179 let siblings = adapter.get_siblings(el);
180 let name = adapter.get_name(el);
181 for s in siblings {
182 if adapter.equals(s, el) {
183 return true;
184 }
185 if adapter.is_tag(s) && adapter.get_name(s) == name {
186 break;
187 }
188 }
189 false
190}
191fn is_last_of_type<A: Adapter>(el: &A::HTMLElement, adapter: &A) -> bool {
192 let siblings = adapter.get_siblings(el);
193 let name = adapter.get_name(el);
194 for s in siblings.iter().rev() {
195 if adapter.equals(s, el) {
196 return true;
197 }
198 if adapter.is_tag(s) && adapter.get_name(s) == name {
199 break;
200 }
201 }
202 false
203}
204fn is_only_of_type<A: Adapter>(el: &A::HTMLElement, adapter: &A) -> bool {
205 let siblings = adapter.get_siblings(el);
206 let name = adapter.get_name(el);
207 let mut others = false;
208 for s in siblings {
209 if adapter.is_tag(s) && !adapter.equals(s, el) && adapter.get_name(s) == name {
210 others = true;
211 break;
212 }
213 }
214 !others
215}
216
217fn match_nth(expr: &NthExpr, index_one: i32) -> bool {
221 match expr {
222 NthExpr::Number(n) => index_one == *n,
223 NthExpr::Odd => index_one % 2 == 1,
224 NthExpr::Even => index_one % 2 == 0,
225 NthExpr::Pattern { a, b } => {
226 let a = *a;
227 let b = *b;
228 if a == 0 {
229 return index_one == b;
230 }
231 if a > 0 {
232 if index_one < b {
233 return false;
234 }
235 (index_one - b) % a == 0
236 } else {
237 let mut k = 0;
238 loop {
239 let val = a * k + b;
240 if val == index_one {
241 return true;
242 }
243 if val < 1 || val > index_one {
244 return false;
245 }
246 k += 1;
247 }
248 }
249 }
250 }
251}
252
253fn match_nth_pseudo<A: Adapter>(
254 el: &A::HTMLElement,
255 adapter: &A,
256 name: &str,
257 expr: &NthExpr,
258) -> bool {
259 let siblings = adapter.get_siblings(el);
260 let mut positions: Vec<&A::HTMLElement> = Vec::new();
261 match name {
262 "nth-child" | "nth-last-child" => {
263 for s in &siblings {
264 if adapter.is_tag(s) {
265 positions.push(*s);
266 }
267 }
268 if name == "nth-child" {
269 if let Some(pos) = positions.iter().position(|p| adapter.equals(p, el)) {
270 return match_nth(expr, (pos as i32) + 1);
271 }
272 } else {
273 if let Some(pos) = positions.iter().rposition(|p| adapter.equals(p, el)) {
274 let rev = positions.len() - pos;
275 return match_nth(expr, rev as i32);
276 }
277 }
278 }
279 "nth-of-type" | "nth-last-of-type" => {
280 let tag = adapter.get_name(el);
281 for s in &siblings {
282 if adapter.is_tag(s) && adapter.get_name(s) == tag {
283 positions.push(*s);
284 }
285 }
286 if name == "nth-of-type" {
287 if let Some(pos) = positions.iter().position(|p| adapter.equals(p, el)) {
288 return match_nth(expr, (pos as i32) + 1);
289 }
290 } else {
291 if let Some(pos) = positions.iter().rposition(|p| adapter.equals(p, el)) {
292 let rev = positions.len() - pos;
293 return match_nth(expr, rev as i32);
294 }
295 }
296 }
297 _ => {}
298 }
299 false
300}
301
302fn has_descendant_with_any<'a, A: Adapter>(
303 el: &A::HTMLElement,
304 adapter: &A,
305 funcs: &Vec<Box<dyn Fn(&A::HTMLElement) -> bool + 'a>>,
306) -> bool {
307 for c in adapter.get_children(el) {
308 for f in funcs {
309 if f(&c) {
310 return true;
311 }
312 }
313 if has_descendant_with_any(c, adapter, funcs) {
314 return true;
315 }
316 }
317 false
318}