use crate::{Cursor, Diagnostic, Kind, KindSet, Parse, Parser, Peek, Result};
use bumpalo::collections::Vec;
pub trait CompoundSelector<'a>: Sized + Parse<'a> {
type SelectorComponent: Parse<'a> + SelectorComponent<'a>;
fn parse_compound_selector_part<I>(p: &mut Parser<'a, I>) -> Result<Option<Self::SelectorComponent>>
where
I: Iterator<Item = Cursor> + Clone,
{
let skip = p.set_skip(KindSet::TRIVIA);
let next = p.peek_n(1);
p.set_skip(skip);
if next == Kind::Eof || next == KindSet::LEFT_CURLY_RIGHT_PAREN_COMMA_OR_SEMICOLON {
return Ok(None);
}
p.parse::<Self::SelectorComponent>().map(Some)
}
fn parse_compound_selector<I>(p: &mut Parser<'a, I>) -> Result<Vec<'a, Self::SelectorComponent>>
where
I: Iterator<Item = Cursor> + Clone,
{
let mut components = Vec::new_in(p.bump());
p.consume_trivia();
while let Some(component) = Self::parse_compound_selector_part(p)? {
components.push(component);
}
Ok(components)
}
}
pub trait SelectorComponent<'a>: Sized {
type Wildcard: Peek<'a> + Parse<'a>;
type Id: Peek<'a> + Parse<'a>;
type Type: Peek<'a> + Parse<'a>;
type PseudoClass: Parse<'a>;
type PseudoElement: Parse<'a>;
type LegacyPseudoElement: Peek<'a> + Parse<'a>;
type Class: Parse<'a>;
type NsType: Parse<'a>;
type Combinator: Parse<'a>;
type Attribute: Parse<'a>;
type FunctionalPseudoClass: Parse<'a>;
type FunctionalPseudoElement: Parse<'a>;
fn build_wildcard(node: Self::Wildcard) -> Self;
fn build_id(node: Self::Id) -> Self;
fn build_class(node: Self::Class) -> Self;
fn build_type(node: Self::Type) -> Self;
fn build_pseudo_class(node: Self::PseudoClass) -> Self;
fn build_pseudo_element(node: Self::PseudoElement) -> Self;
fn build_legacy_pseudo_element(node: Self::LegacyPseudoElement) -> Self;
fn build_ns_type(node: Self::NsType) -> Self;
fn build_combinator(node: Self::Combinator) -> Self;
fn build_attribute(node: Self::Attribute) -> Self;
fn build_functional_pseudo_class(node: Self::FunctionalPseudoClass) -> Self;
fn build_functional_pseudo_element(node: Self::FunctionalPseudoElement) -> Self;
fn parse_selector_component<I>(p: &mut Parser<'a, I>) -> Result<Self>
where
I: Iterator<Item = Cursor> + Clone,
{
let skip = p.set_skip(KindSet::COMMENTS);
let c = p.peek_n(1);
let t = c.token();
match t.kind() {
Kind::Ident => match p.peek_n(2) {
t if t == '|' => {
p.set_skip(skip);
p.parse::<Self::NsType>().map(Self::build_ns_type)
}
_ => {
p.set_skip(skip);
if Self::Type::peek(p, c) {
Ok(Self::build_type(p.parse::<Self::Type>()?))
} else {
Err(Diagnostic::new(c, Diagnostic::unexpected_tag))?
}
}
},
Kind::Hash if t.hash_is_id_like() => {
p.set_skip(skip);
if Self::Id::peek(p, c) {
Ok(Self::build_id(p.parse::<Self::Id>()?))
} else {
Err(Diagnostic::new(c, Diagnostic::unexpected_id))?
}
}
Kind::LeftSquare => {
p.set_skip(skip);
p.parse::<Self::Attribute>().map(Self::build_attribute)
}
Kind::Delim => match t.char().unwrap() {
'.' => {
let c = p.peek_n(2);
p.set_skip(skip);
match c.token().kind() {
Kind::Ident => p.parse::<Self::Class>().map(Self::build_class),
_ => Err(Diagnostic::new(c, Diagnostic::expected_ident))?,
}
}
'*' => {
let t = p.peek_n(2);
p.set_skip(skip);
if t == '|' {
p.parse::<Self::NsType>().map(Self::build_ns_type)
} else {
Ok(Self::build_wildcard(p.parse::<Self::Wildcard>()?))
}
}
_ => {
p.set_skip(skip);
let value = p.parse::<Self::Combinator>().map(Self::build_combinator);
p.set_skip(KindSet::WHITESPACE);
p.consume_trivia_as_leading();
p.set_skip(skip);
value
}
},
Kind::Colon => {
let c2 = p.peek_n(2);
match c2.token().kind() {
Kind::Colon => {
let c3 = p.peek_n(3);
p.set_skip(skip);
match c3.token().kind() {
Kind::Ident => p.parse::<Self::PseudoElement>().map(Self::build_pseudo_element),
Kind::Function => {
p.parse::<Self::FunctionalPseudoElement>().map(Self::build_functional_pseudo_element)
}
_ => Err(Diagnostic::new(c3, Diagnostic::unexpected))?,
}
}
Kind::Ident => {
p.set_skip(skip);
if Self::LegacyPseudoElement::peek(p, c) {
p.parse::<Self::LegacyPseudoElement>().map(Self::build_legacy_pseudo_element)
} else {
p.parse::<Self::PseudoClass>().map(Self::build_pseudo_class)
}
}
Kind::Function => {
p.set_skip(skip);
p.parse::<Self::FunctionalPseudoClass>().map(Self::build_functional_pseudo_class)
}
_ => Err(Diagnostic::new(c2, Diagnostic::unexpected))?,
}
}
_ => {
if t.kind() == Kind::Whitespace {
p.set_skip(KindSet::TRIVIA);
let next = p.peek_n(1);
let next_is_explicit_combinator = match next.token().kind() {
Kind::Delim => matches!(next.token().char(), Some('>' | '+' | '~' | '|' | '&')),
_ => false,
};
if next_is_explicit_combinator {
p.consume_trivia_as_leading();
p.set_skip(skip);
return Self::parse_selector_component(p);
}
p.set_skip(skip);
}
let value = p.parse::<Self::Combinator>().map(Self::build_combinator);
p.set_skip(KindSet::WHITESPACE);
p.consume_trivia_as_leading();
p.set_skip(skip);
value
}
}
}
}