use crate::parser::{Combinator, Component, SelectorImpl};
use crate::sink::Push;
use smallvec::{self, SmallVec};
use std::cmp;
use std::iter;
use std::ptr;
use std::slice;
#[derive(Debug)]
pub struct SelectorBuilder<'i, Impl: SelectorImpl<'i>> {
simple_selectors: SmallVec<[Component<'i, Impl>; 32]>,
combinators: SmallVec<[(Combinator, usize); 16]>,
current_len: usize,
}
impl<'i, Impl: SelectorImpl<'i>> Default for SelectorBuilder<'i, Impl> {
#[inline(always)]
fn default() -> Self {
SelectorBuilder {
simple_selectors: SmallVec::new(),
combinators: SmallVec::new(),
current_len: 0,
}
}
}
impl<'i, Impl: SelectorImpl<'i>> Push<Component<'i, Impl>> for SelectorBuilder<'i, Impl> {
fn push(&mut self, value: Component<'i, Impl>) {
self.push_simple_selector(value);
}
}
impl<'i, Impl: SelectorImpl<'i>> SelectorBuilder<'i, Impl> {
#[inline(always)]
pub fn push_simple_selector(&mut self, ss: Component<'i, Impl>) {
assert!(!ss.is_combinator());
self.simple_selectors.push(ss);
self.current_len += 1;
}
#[inline(always)]
pub fn push_combinator(&mut self, c: Combinator) {
self.combinators.push((c, self.current_len));
self.current_len = 0;
}
#[inline(always)]
pub fn has_combinators(&self) -> bool {
!self.combinators.is_empty()
}
#[inline(always)]
pub fn build(
&mut self,
parsed_pseudo: bool,
parsed_slotted: bool,
parsed_part: bool,
) -> (SpecificityAndFlags, Vec<Component<'i, Impl>>) {
let specificity = specificity(self.simple_selectors.iter());
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 })
}
#[inline(always)]
pub fn build_with_specificity_and_flags(
&mut self,
spec: SpecificityAndFlags,
) -> (SpecificityAndFlags, Vec<Component<'i, Impl>>) {
let raw_simple_selectors: *const [Component<Impl>] = &*self.simple_selectors;
unsafe {
self.simple_selectors.set_len(0)
}
let (rest, current) = split_from_end(unsafe { &*raw_simple_selectors }, self.current_len);
let iter = SelectorBuilderIter {
current_simple_selectors: current.iter(),
rest_of_simple_selectors: rest,
combinators: self.combinators.drain(..).rev(),
};
(spec, iter.collect())
}
}
struct SelectorBuilderIter<'a, 'i, Impl: SelectorImpl<'i>> {
current_simple_selectors: slice::Iter<'a, Component<'i, Impl>>,
rest_of_simple_selectors: &'a [Component<'i, Impl>],
combinators: iter::Rev<smallvec::Drain<'a, [(Combinator, usize); 16]>>,
}
impl<'a, 'i, Impl: SelectorImpl<'i>> ExactSizeIterator for SelectorBuilderIter<'a, 'i, Impl> {
fn len(&self) -> usize {
self.current_simple_selectors.len() + self.rest_of_simple_selectors.len() + self.combinators.len()
}
}
impl<'a, 'i, Impl: SelectorImpl<'i>> Iterator for SelectorBuilderIter<'a, 'i, Impl> {
type Item = Component<'i, Impl>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
if let Some(simple_selector_ref) = self.current_simple_selectors.next() {
unsafe { Some(ptr::read(simple_selector_ref)) }
} else {
self.combinators.next().map(|(combinator, len)| {
let (rest, current) = split_from_end(self.rest_of_simple_selectors, len);
self.rest_of_simple_selectors = rest;
self.current_simple_selectors = current.iter();
Component::Combinator(combinator)
})
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len(), Some(self.len()))
}
}
fn split_from_end<T>(s: &[T], at: usize) -> (&[T], &[T]) {
s.split_at(s.len() - at)
}
bitflags! {
#[derive(Default)]
pub (crate) struct SelectorFlags : u8 {
const HAS_PSEUDO = 1 << 0;
const HAS_SLOTTED = 1 << 1;
const HAS_PART = 1 << 2;
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SpecificityAndFlags {
pub(crate) specificity: u32,
pub(crate) flags: SelectorFlags,
}
impl SpecificityAndFlags {
#[inline]
pub fn specificity(&self) -> u32 {
self.specificity
}
#[inline]
pub fn has_pseudo_element(&self) -> bool {
self.flags.intersects(SelectorFlags::HAS_PSEUDO)
}
#[inline]
pub fn is_slotted(&self) -> bool {
self.flags.intersects(SelectorFlags::HAS_SLOTTED)
}
#[inline]
pub fn is_part(&self) -> bool {
self.flags.intersects(SelectorFlags::HAS_PART)
}
}
const MAX_10BIT: u32 = (1u32 << 10) - 1;
#[derive(Add, AddAssign, Clone, Copy, Default, Eq, Ord, PartialEq, PartialOrd)]
struct Specificity {
id_selectors: u32,
class_like_selectors: u32,
element_selectors: u32,
}
impl From<u32> for Specificity {
#[inline]
fn from(value: u32) -> Specificity {
assert!(value <= MAX_10BIT << 20 | MAX_10BIT << 10 | MAX_10BIT);
Specificity {
id_selectors: value >> 20,
class_like_selectors: (value >> 10) & MAX_10BIT,
element_selectors: value & MAX_10BIT,
}
}
}
impl From<Specificity> for u32 {
#[inline]
fn from(specificity: Specificity) -> u32 {
cmp::min(specificity.id_selectors, MAX_10BIT) << 20
| cmp::min(specificity.class_like_selectors, MAX_10BIT) << 10
| cmp::min(specificity.element_selectors, MAX_10BIT)
}
}
fn specificity<'i, Impl>(iter: slice::Iter<Component<'i, Impl>>) -> u32
where
Impl: SelectorImpl<'i>,
{
complex_selector_specificity(iter).into()
}
fn complex_selector_specificity<'i, Impl>(iter: slice::Iter<Component<'i, Impl>>) -> Specificity
where
Impl: SelectorImpl<'i>,
{
fn simple_selector_specificity<'i, Impl>(simple_selector: &Component<'i, Impl>, specificity: &mut Specificity)
where
Impl: SelectorImpl<'i>,
{
match *simple_selector {
Component::Combinator(..) => {
unreachable!("Found combinator in simple selectors vector?");
}
Component::Part(..) | Component::PseudoElement(..) | Component::LocalName(..) => {
specificity.element_selectors += 1
}
Component::Slotted(ref selector) => {
specificity.element_selectors += 1;
*specificity += Specificity::from(selector.specificity());
}
Component::Host(ref selector) => {
specificity.class_like_selectors += 1;
if let Some(ref selector) = *selector {
*specificity += Specificity::from(selector.specificity());
}
}
Component::ID(..) => {
specificity.id_selectors += 1;
}
Component::Class(..)
| Component::AttributeInNoNamespace { .. }
| Component::AttributeInNoNamespaceExists { .. }
| Component::AttributeOther(..)
| Component::FirstChild
| Component::LastChild
| Component::OnlyChild
| Component::Root
| Component::Empty
| Component::Scope
| Component::NthChild(..)
| Component::NthLastChild(..)
| Component::NthCol(..)
| Component::NthLastCol(..)
| Component::NthOfType(..)
| Component::NthLastOfType(..)
| Component::FirstOfType
| Component::LastOfType
| Component::OnlyOfType
| Component::NonTSPseudoClass(..) => {
specificity.class_like_selectors += 1;
}
Component::Negation(ref list) | Component::Is(ref list) | Component::Any(_, ref list) => {
let mut max = 0;
for selector in &**list {
max = std::cmp::max(selector.specificity(), max);
}
*specificity += Specificity::from(max);
}
Component::Where(..)
| Component::Has(..)
| Component::ExplicitUniversalType
| Component::ExplicitAnyNamespace
| Component::ExplicitNoNamespace
| Component::DefaultNamespace(..)
| Component::Namespace(..) => {
}
Component::Nesting => {
}
}
}
let mut specificity = Default::default();
for simple_selector in iter {
simple_selector_specificity(&simple_selector, &mut specificity);
}
specificity
}