css_parse/traits/
selectors.rs

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