node_html_parser/css_select/
general.rs

1use 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			// 预编译子选择器(:is/:where/:not/:has)子序列为闭包列表,避免每次重新构建
65			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						// (移除调试打印)
72						let mut inner: Box<dyn Fn(&A::HTMLElement) -> bool + 'a> =
73							Box::new(|_| true);
74						// 对子序列简单 tokens 排序(忽略 traversal 拆分,因为内部多为复合)
75						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
148// -------- 辅助:结构伪类判定(贴近 JS pseudos.ts 实现) --------
149fn 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
217// 评估 :not 内部序列(简易:顺序执行 simple tokens;若含 traversal 当前实现不足但已有基础)
218// 已弃用的 eval_simple_chain 被预编译子选择器取代
219
220fn 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}