css_parse/traits/
selectors.rs

1use crate::{Cursor, Diagnostic, Kind, KindSet, Parse, Parser, Peek, Result};
2use bumpalo::collections::Vec;
3
4pub trait CompoundSelector<'a>: Sized + Parse<'a> {
5	// SelectorComponent represents a Selector, or Combinator.
6	// https://drafts.csswg.org/selectors-4/#typedef-combinator
7	// https://drafts.csswg.org/selectors-4/#typedef-type-selector
8	// https://drafts.csswg.org/selectors-4/#typedef-subclass-selector
9	// https://drafts.csswg.org/selectors-4/#typedef-pseudo-element-selector
10	type SelectorComponent: Parse<'a> + SelectorComponent<'a>;
11
12	fn parse_compound_selector<I>(p: &mut Parser<'a, I>) -> Result<Vec<'a, Self::SelectorComponent>>
13	where
14		I: Iterator<Item = Cursor> + Clone,
15	{
16		let mut components = Vec::new_in(p.bump());
17		// Trim leading whitespace
18		p.consume_trivia();
19		loop {
20			// If a stop token has been reached, break the loop
21			if p.at_end() || p.peek_n(1) == KindSet::LEFT_CURLY_RIGHT_PAREN_COMMA_OR_SEMICOLON {
22				break;
23			}
24			components.push(p.parse::<Self::SelectorComponent>()?);
25		}
26		Ok(components)
27	}
28}
29
30pub trait SelectorComponent<'a>: Sized {
31	type Wildcard: Peek<'a> + Parse<'a>;
32	type Id: Peek<'a> + Parse<'a>;
33	type Type: Peek<'a> + Parse<'a>;
34	type PseudoClass: Parse<'a>;
35	type PseudoElement: Parse<'a>;
36	type LegacyPseudoElement: Peek<'a> + Parse<'a>;
37	type Class: Parse<'a>;
38	type NsType: Parse<'a>;
39	type Combinator: Parse<'a>;
40	type Attribute: Parse<'a>;
41	type FunctionalPseudoClass: Parse<'a>;
42	type FunctionalPseudoElement: Parse<'a>;
43
44	fn build_wildcard(node: Self::Wildcard) -> Self;
45	fn build_id(node: Self::Id) -> Self;
46	fn build_class(node: Self::Class) -> Self;
47	fn build_type(node: Self::Type) -> Self;
48	fn build_pseudo_class(node: Self::PseudoClass) -> Self;
49	fn build_pseudo_element(node: Self::PseudoElement) -> Self;
50	fn build_legacy_pseudo_element(node: Self::LegacyPseudoElement) -> Self;
51	fn build_ns_type(node: Self::NsType) -> Self;
52	fn build_combinator(node: Self::Combinator) -> Self;
53	fn build_attribute(node: Self::Attribute) -> Self;
54	fn build_functional_pseudo_class(node: Self::FunctionalPseudoClass) -> Self;
55	fn build_functional_pseudo_element(node: Self::FunctionalPseudoElement) -> Self;
56
57	fn parse_selector_component<I>(p: &mut Parser<'a, I>) -> Result<Self>
58	where
59		I: Iterator<Item = Cursor> + Clone,
60	{
61		let skip = p.set_skip(KindSet::COMMENTS);
62		let c = p.peek_n(1);
63		let t = c.token();
64		match t.kind() {
65			Kind::Ident => match p.peek_n(2) {
66				t if t == '|' => {
67					p.set_skip(skip);
68					p.parse::<Self::NsType>().map(Self::build_ns_type)
69				}
70				_ => {
71					p.set_skip(skip);
72					if Self::Type::peek(p, c) {
73						Ok(Self::build_type(p.parse::<Self::Type>()?))
74					} else {
75						Err(Diagnostic::new(c, Diagnostic::unexpected_tag))?
76					}
77				}
78			},
79			Kind::Hash if t.hash_is_id_like() => {
80				p.set_skip(skip);
81				if Self::Id::peek(p, c) {
82					Ok(Self::build_id(p.parse::<Self::Id>()?))
83				} else {
84					Err(Diagnostic::new(c, Diagnostic::unexpected_id))?
85				}
86			}
87			Kind::LeftSquare => {
88				p.set_skip(skip);
89				p.parse::<Self::Attribute>().map(Self::build_attribute)
90			}
91			Kind::Delim => match t.char().unwrap() {
92				'.' => {
93					let c = p.peek_n(2);
94					p.set_skip(skip);
95					match c.token().kind() {
96						Kind::Ident => p.parse::<Self::Class>().map(Self::build_class),
97						_ => Err(Diagnostic::new(c, Diagnostic::expected_ident))?,
98					}
99				}
100				'*' => {
101					let t = p.peek_n(2);
102					p.set_skip(skip);
103					if t == '|' {
104						p.parse::<Self::NsType>().map(Self::build_ns_type)
105					} else {
106						Ok(Self::build_wildcard(p.parse::<Self::Wildcard>()?))
107					}
108				}
109				_ => {
110					p.set_skip(skip);
111					p.parse::<Self::Combinator>().map(Self::build_combinator)
112				}
113			},
114			Kind::Colon => {
115				let c2 = p.peek_n(2);
116				match c2.token().kind() {
117					Kind::Colon => {
118						let c3 = p.peek_n(3);
119						p.set_skip(skip);
120						match c3.token().kind() {
121							Kind::Ident => p.parse::<Self::PseudoElement>().map(Self::build_pseudo_element),
122							Kind::Function => {
123								p.parse::<Self::FunctionalPseudoElement>().map(Self::build_functional_pseudo_element)
124							}
125							_ => Err(Diagnostic::new(c3, Diagnostic::unexpected))?,
126						}
127					}
128					Kind::Ident => {
129						p.set_skip(skip);
130						if Self::LegacyPseudoElement::peek(p, c) {
131							p.parse::<Self::LegacyPseudoElement>().map(Self::build_legacy_pseudo_element)
132						} else {
133							p.parse::<Self::PseudoClass>().map(Self::build_pseudo_class)
134						}
135					}
136					Kind::Function => {
137						p.set_skip(skip);
138						p.parse::<Self::FunctionalPseudoClass>().map(Self::build_functional_pseudo_class)
139					}
140					_ => Err(Diagnostic::new(c2, Diagnostic::unexpected))?,
141				}
142			}
143			_ => {
144				let value = p.parse::<Self::Combinator>().map(Self::build_combinator);
145				// Given descendant combinators cannot appear in sequence with other combinators, we can safely eat trivia here
146				// in order to remove unecessary conjoined descendant combinators
147				p.set_skip(KindSet::WHITESPACE);
148				p.consume_trivia();
149				p.set_skip(skip);
150				value
151			}
152		}
153	}
154}