use super::convert::selector_to_internal;
use super::helpers::selectors::{is_traversal, sort_rules};
use super::legacy::{apply_selector_from_el, parse_selector_list_public};
use super::types::{Adapter, CompiledQuery, Options};
use crate::css_select::general::compile_general_selector;
use crate::css_select::types::InternalSelector;
use crate::dom::element::HTMLElement;
pub fn compile_token<'a, A: Adapter>(
selector: &str,
_options: &Options<A>,
root: &'a HTMLElement,
) -> CompiledQuery<'a, A> {
let legacy_list = parse_selector_list_public(selector);
CompiledQuery::new(move |el: &A::HTMLElement| {
let real = el as *const A::HTMLElement as *const HTMLElement;
let real_ref = unsafe { &*real };
for sel in &legacy_list {
if apply_selector_from_el(root, real_ref, sel) {
return true;
}
}
false
})
}
fn reorder_with_sort(seq: &[InternalSelector]) -> Vec<InternalSelector> {
let mut out: Vec<InternalSelector> = Vec::with_capacity(seq.len());
let mut buffer: Vec<InternalSelector> = Vec::new();
for tok in seq {
if is_traversal(tok) {
if !buffer.is_empty() {
sort_rules(&mut buffer);
out.extend(buffer.drain(..));
}
out.push(tok.clone());
} else {
buffer.push(tok.clone());
}
}
if !buffer.is_empty() {
sort_rules(&mut buffer);
out.extend(buffer.drain(..));
}
out
}
pub fn compile_internal_new<'a, A: Adapter + 'a>(
selector: &str,
adapter: &'a A,
) -> CompiledQuery<'a, A> {
let parsed = parse_selector_list_public(selector);
if parsed.is_empty() {
return CompiledQuery::new(|_| false);
}
let mut chains: Vec<Box<dyn Fn(&A::HTMLElement) -> bool + 'a>> = Vec::new();
for sel in parsed {
let internal_groups = selector_to_internal(&sel); for seq in internal_groups {
if seq.is_empty() {
continue;
}
let mut seq_work = seq.clone();
let has_scope = seq_work
.iter()
.any(|t| matches!(t, InternalSelector::Pseudo { name, .. } if name == "scope"));
if !has_scope {
if !seq_work.first().map(|t| is_traversal(t)).unwrap_or(false) {
seq_work.insert(0, InternalSelector::Descendant); seq_work.insert(
0,
InternalSelector::Pseudo {
name: "scope".into(),
data: crate::css_select::types::PseudoData::None,
},
);
}
}
let reordered = reorder_with_sort(&seq_work);
let mut next: Box<dyn Fn(&A::HTMLElement) -> bool + 'a> = Box::new(|_| true);
for token in reordered.iter() {
next = compile_general_selector(next, token, adapter);
}
chains.push(next);
}
}
if chains.is_empty() {
return CompiledQuery::new(|_| false);
}
let combined = move |el: &A::HTMLElement| {
for f in &chains {
if f(el) {
return true;
}
}
false
};
CompiledQuery::new(combined)
}