css_parse/traits/
selectors.rs1use crate::{Build, Cursor, Kind, KindSet, Parse, Parser, Peek, Result, diagnostics};
2use bumpalo::collections::Vec;
3
4pub trait CompoundSelector<'a>: Sized + Parse<'a> {
5 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 p.consume_trivia();
16 loop {
17 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 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}