node_html_parser/css_select/
compile.rs1use super::convert::selector_to_internal;
2use super::helpers::selectors::{is_traversal, sort_rules};
3use super::legacy::{apply_selector_from_el, parse_selector_list_public};
4use super::types::{Adapter, CompiledQuery, Options};
5use crate::css_select::general::compile_general_selector;
6use crate::css_select::types::InternalSelector;
7use crate::dom::element::HTMLElement;
8
9pub fn compile_token<'a, A: Adapter>(
12 selector: &str,
13 _options: &Options<A>,
14 root: &'a HTMLElement,
15) -> CompiledQuery<'a, A> {
16 let legacy_list = parse_selector_list_public(selector);
17 CompiledQuery::new(move |el: &A::HTMLElement| {
18 let real = el as *const A::HTMLElement as *const HTMLElement;
19 let real_ref = unsafe { &*real };
20 for sel in &legacy_list {
21 if apply_selector_from_el(root, real_ref, sel) {
22 return true;
23 }
24 }
25 false
26 })
27}
28
29fn reorder_with_sort(seq: &[InternalSelector]) -> Vec<InternalSelector> {
38 let mut out: Vec<InternalSelector> = Vec::with_capacity(seq.len());
39 let mut buffer: Vec<InternalSelector> = Vec::new();
40 for tok in seq {
41 if is_traversal(tok) {
42 if !buffer.is_empty() {
43 sort_rules(&mut buffer);
44 out.extend(buffer.drain(..));
45 }
46 out.push(tok.clone());
47 } else {
48 buffer.push(tok.clone());
49 }
50 }
51 if !buffer.is_empty() {
52 sort_rules(&mut buffer);
53 out.extend(buffer.drain(..));
54 }
55 out
56}
57
58pub fn compile_internal_new<'a, A: Adapter + 'a>(
59 selector: &str,
60 adapter: &'a A,
61) -> CompiledQuery<'a, A> {
62 let parsed = parse_selector_list_public(selector);
63 if parsed.is_empty() {
64 return CompiledQuery::new(|_| false);
65 }
66
67 let mut chains: Vec<Box<dyn Fn(&A::HTMLElement) -> bool + 'a>> = Vec::new();
69 for sel in parsed {
70 let internal_groups = selector_to_internal(&sel); for seq in internal_groups {
72 if seq.is_empty() {
73 continue;
74 }
75 let mut seq_work = seq.clone();
77 let has_scope = seq_work
78 .iter()
79 .any(|t| matches!(t, InternalSelector::Pseudo { name, .. } if name == "scope"));
80 if !has_scope {
81 if !seq_work.first().map(|t| is_traversal(t)).unwrap_or(false) {
82 seq_work.insert(0, InternalSelector::Descendant); seq_work.insert(
84 0,
85 InternalSelector::Pseudo {
86 name: "scope".into(),
87 data: crate::css_select::types::PseudoData::None,
88 },
89 );
90 }
91 }
92 let reordered = reorder_with_sort(&seq_work);
93 let mut next: Box<dyn Fn(&A::HTMLElement) -> bool + 'a> = Box::new(|_| true);
94 for token in reordered.iter() {
95 next = compile_general_selector(next, token, adapter);
96 }
98 chains.push(next);
99 }
100 }
101 if chains.is_empty() {
102 return CompiledQuery::new(|_| false);
103 }
104 let combined = move |el: &A::HTMLElement| {
105 for f in &chains {
106 if f(el) {
107 return true;
108 }
109 }
110 false
111 };
112 CompiledQuery::new(combined)
113}