use crate::SmallList;
pub use crate::{PrintErr, Printer};
use bun_alloc::ArenaPtr;
use crate::selector::parser::{
BunSelectorImpl as ValidSelectorImpl, Combinator, GenericComponent, SelectorFlags,
SpecificityAndFlags, compute_specificity,
};
pub struct SelectorBuilder<Impl: ValidSelectorImpl> {
simple_selectors: SmallList<GenericComponent<Impl>, 32>,
combinators: SmallList<(Combinator, usize), 32>,
current_len: usize,
alloc: ArenaPtr,
}
pub struct BuildResult<Impl: ValidSelectorImpl> {
pub specificity_and_flags: SpecificityAndFlags,
pub components: Vec<GenericComponent<Impl>, ArenaPtr>,
}
impl<Impl: ValidSelectorImpl> Default for SelectorBuilder<Impl> {
#[inline]
fn default() -> Self {
Self::init_in(ArenaPtr::global())
}
}
impl<Impl: ValidSelectorImpl> SelectorBuilder<Impl> {
#[inline]
pub(crate) fn init_in(alloc: ArenaPtr) -> Self {
Self {
simple_selectors: SmallList::default(),
combinators: SmallList::default(),
current_len: 0,
alloc,
}
}
#[inline]
pub(crate) fn has_combinators(&self) -> bool {
self.combinators.len() > 0
}
#[inline]
pub(crate) fn push_combinator(&mut self, combinator: Combinator) {
self.combinators.append((combinator, self.current_len));
self.current_len = 0;
}
pub(crate) fn push_simple_selector(&mut self, ss: GenericComponent<Impl>) {
debug_assert!(!ss.is_combinator());
self.simple_selectors.append(ss);
self.current_len += 1;
}
pub(crate) fn add_nesting_prefix(&mut self) {
self.combinators.insert(0, (Combinator::Descendant, 1));
self.simple_selectors.insert(0, GenericComponent::Nesting);
}
pub(crate) fn build(
&mut self,
parsed_pseudo: bool,
parsed_slotted: bool,
parsed_part: bool,
) -> BuildResult<Impl> {
let specificity = compute_specificity::<Impl>(self.simple_selectors.slice());
let mut flags = SelectorFlags::empty();
if parsed_pseudo {
flags |= SelectorFlags::HAS_PSEUDO;
}
if parsed_slotted {
flags |= SelectorFlags::HAS_SLOTTED;
}
if parsed_part {
flags |= SelectorFlags::HAS_PART;
}
self.build_with_specificity_and_flags(SpecificityAndFlags { specificity, flags })
}
pub(crate) fn build_with_specificity_and_flags(
&mut self,
spec: SpecificityAndFlags,
) -> BuildResult<Impl> {
let combinators_len = self.combinators.len();
let (rest, current) = split_from_end::<GenericComponent<Impl>>(
self.simple_selectors.slice(),
self.current_len,
);
let combinators = self.combinators.slice();
let mut components: Vec<GenericComponent<Impl>, ArenaPtr> = Vec::new_in(self.alloc);
let mut current_simple_selectors_i: usize = 0;
let mut combinator_i: i64 = i64::from(combinators_len) - 1;
let mut rest_of_simple_selectors = rest;
let mut current_simple_selectors = current;
loop {
if current_simple_selectors_i < current_simple_selectors.len() {
let moved = unsafe {
core::ptr::read(&raw const current_simple_selectors[current_simple_selectors_i])
};
components.push(moved);
current_simple_selectors_i += 1;
} else {
if combinator_i >= 0 {
let (combo, len) =
combinators[usize::try_from(combinator_i).expect("int cast")];
let (rest2, current2) =
split_from_end::<GenericComponent<Impl>>(rest_of_simple_selectors, len);
rest_of_simple_selectors = rest2;
current_simple_selectors_i = 0;
current_simple_selectors = current2;
combinator_i -= 1;
components.push(GenericComponent::Combinator(combo));
continue;
}
break;
}
}
self.simple_selectors.set_len(0);
self.combinators.set_len(0);
BuildResult {
specificity_and_flags: spec,
components,
}
}
}
pub(crate) fn split_from_end<T>(s: &[T], at: usize) -> (&[T], &[T]) {
let midpoint = s.len() - at;
(&s[0..midpoint], &s[midpoint..])
}