1use crate::CssAtomSet;
2use css_lexer::Kind;
3use css_parse::{CommaSeparated, Cursor, Diagnostic, Parse, Parser, Peek, Result as ParserResult, T};
4use csskit_derives::{Parse, Peek, SemanticEq, ToCursors, ToSpan};
5
6use super::{ForgivingSelector, Nth, RelativeSelector, SelectorList};
7
8macro_rules! apply_functional_pseudo_class {
9 ($macro: ident) => {
10 $macro! {
11 Dir(DirPseudoFunction) CssAtomSet::Dir,
12 Has(HasPseudoFunction<'a>) CssAtomSet::Has,
13 Heading(HeadingPseudoFunction<'a>) CssAtomSet::Heading,
14 Host(HostPseudoFunction<'a>) CssAtomSet::Host,
15 HostContext(HostContextPseudoFunction<'a>) CssAtomSet::HostContext,
16 Is(IsPseudoFunction<'a>) CssAtomSet::Is,
17 Lang(LangPseudoFunction<'a>) CssAtomSet::Lang,
18 Not(NotPseudoFunction<'a>) CssAtomSet::Not,
19 NthChild(NthChildPseudoFunction) CssAtomSet::NthChild,
20 NthCol(NthColPseudoFunction) CssAtomSet::NthCol,
21 NthLastChild(NthLastChildPseudoFunction) CssAtomSet::NthLastChild,
22 NthLastCol(NthLastColPseudoFunction) CssAtomSet::NthLastCol,
23 NthLastOfType(NthLastOfTypePseudoFunction) CssAtomSet::NthLastOfType,
24 NthOfType(NthOfTypePseudoFunction) CssAtomSet::NthOfType,
25 State(StatePseudoFunction) CssAtomSet::State,
26 Where(WherePseudoFunction<'a>) CssAtomSet::Where,
27 }
28 };
29}
30
31macro_rules! define_functional_pseudo_class {
32 ( $($ident: ident($ty: ty) $pat: pat $(,)*)+ ) => {
33 #[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable))]
34 #[derive( ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
35 #[cfg_attr(feature = "css_feature_data", derive(::csskit_derives::ToCSSFeature), css_feature("css.selectors"))]
36 #[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
37 pub enum FunctionalPseudoClass<'a> {
38 $($ident($ty),)+
39 }
40 }
41}
42apply_functional_pseudo_class!(define_functional_pseudo_class);
43
44impl<'a> Peek<'a> for FunctionalPseudoClass<'a> {
45 fn peek<I>(p: &Parser<'a, I>, c: css_lexer::Cursor) -> bool
46 where
47 I: Iterator<Item = Cursor> + Clone,
48 {
49 <T![:]>::peek(p, c) && p.peek_n(2) == Kind::Function
50 }
51}
52
53impl<'a> Parse<'a> for FunctionalPseudoClass<'a> {
54 fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
55 where
56 I: Iterator<Item = Cursor> + Clone,
57 {
58 macro_rules! match_keyword {
59 ( $($ident: ident($ty: ty) $pat: pat $(,)*)+ ) => {
60 match p.to_atom::<CssAtomSet>(p.peek_n(2)) {
61 $($pat => p.parse::<$ty>().map(Self::$ident),)+
62 _ => Err(Diagnostic::new(p.next(), Diagnostic::unexpected))?
63 }
64 }
65 }
66 apply_functional_pseudo_class!(match_keyword)
67 }
68}
69
70#[derive(Parse, ToSpan, ToCursors, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
71#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
72#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
73pub struct DirPseudoFunction {
74 pub colon: T![:],
75 #[atom(CssAtomSet::Dir)]
76 pub function: T![Function],
77 pub value: DirValue,
78 pub close: Option<T![')']>,
79}
80
81#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
82#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
83#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(skip))]
84pub enum DirValue {
85 #[atom(CssAtomSet::Rtl)]
86 Rtl(T![Ident]),
87 #[atom(CssAtomSet::Ltr)]
88 Ltr(T![Ident]),
89}
90
91#[derive(Parse, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
92#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
93#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
94pub struct HasPseudoFunction<'a> {
95 #[cfg_attr(feature = "visitable", visit(skip))]
96 pub colon: T![:],
97 #[cfg_attr(feature = "visitable", visit(skip))]
98 #[atom(CssAtomSet::Has)]
99 pub function: T![Function],
100 pub value: RelativeSelector<'a>,
101 #[cfg_attr(feature = "visitable", visit(skip))]
102 pub close: Option<T![')']>,
103}
104
105#[derive(Parse, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
106#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
107#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
108pub struct HostPseudoFunction<'a> {
109 #[cfg_attr(feature = "visitable", visit(skip))]
110 pub colon: T![:],
111 #[cfg_attr(feature = "visitable", visit(skip))]
112 #[atom(CssAtomSet::Host)]
113 pub function: T![Function],
114 pub value: SelectorList<'a>,
115 #[cfg_attr(feature = "visitable", visit(skip))]
116 pub close: Option<T![')']>,
117}
118
119#[derive(Parse, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
120#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
121#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
122pub struct HostContextPseudoFunction<'a> {
123 #[cfg_attr(feature = "visitable", visit(skip))]
124 pub colon: T![:],
125 #[cfg_attr(feature = "visitable", visit(skip))]
126 #[atom(CssAtomSet::HostContext)]
127 pub function: T![Function],
128 pub value: SelectorList<'a>,
129 #[cfg_attr(feature = "visitable", visit(skip))]
130 pub close: Option<T![')']>,
131}
132
133#[derive(Parse, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
134#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
135#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
136pub struct IsPseudoFunction<'a> {
137 #[cfg_attr(feature = "visitable", visit(skip))]
138 pub colon: T![:],
139 #[cfg_attr(feature = "visitable", visit(skip))]
140 #[atom(CssAtomSet::Is)]
141 pub function: T![Function],
142 pub value: ForgivingSelector<'a>,
143 #[cfg_attr(feature = "visitable", visit(skip))]
144 pub close: Option<T![')']>,
145}
146
147#[derive(Parse, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
148#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
149#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
150pub struct LangPseudoFunction<'a> {
151 pub colon: T![:],
152 #[atom(CssAtomSet::Lang)]
153 pub function: T![Function],
154 pub value: LangValues<'a>,
155 pub close: Option<T![')']>,
156}
157
158#[derive(ToSpan, Parse, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
159#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
160pub struct LangValues<'a>(pub CommaSeparated<'a, LangValue>);
161
162#[derive(Parse, ToSpan, Peek, ToCursors, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
163#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
164pub enum LangValue {
165 Ident(T![Ident]),
166 String(T![String]),
167}
168
169#[derive(Parse, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
170#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
171#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
172pub struct NotPseudoFunction<'a> {
173 #[cfg_attr(feature = "visitable", visit(skip))]
174 pub colon: T![:],
175 #[cfg_attr(feature = "visitable", visit(skip))]
176 #[atom(CssAtomSet::Not)]
177 pub function: T![Function],
178 pub value: SelectorList<'a>,
179 #[cfg_attr(feature = "visitable", visit(skip))]
180 pub close: Option<T![')']>,
181}
182
183#[derive(Parse, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
184#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
185#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
186pub struct NthChildPseudoFunction {
187 #[cfg_attr(feature = "visitable", visit(skip))]
188 pub colon: T![:],
189 #[cfg_attr(feature = "visitable", visit(skip))]
190 #[atom(CssAtomSet::NthChild)]
191 pub function: T![Function],
192 pub value: Nth,
193 #[cfg_attr(feature = "visitable", visit(skip))]
194 pub close: Option<T![')']>,
195}
196
197#[derive(Parse, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
198#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
199#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
200pub struct NthColPseudoFunction {
201 #[cfg_attr(feature = "visitable", visit(skip))]
202 pub colon: T![:],
203 #[cfg_attr(feature = "visitable", visit(skip))]
204 #[atom(CssAtomSet::NthCol)]
205 pub function: T![Function],
206 pub value: Nth,
207 #[cfg_attr(feature = "visitable", visit(skip))]
208 pub close: Option<T![')']>,
209}
210
211#[derive(Parse, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
212#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
213#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
214pub struct NthLastChildPseudoFunction {
215 #[cfg_attr(feature = "visitable", visit(skip))]
216 pub colon: T![:],
217 #[cfg_attr(feature = "visitable", visit(skip))]
218 #[atom(CssAtomSet::NthLastChild)]
219 pub function: T![Function],
220 pub value: Nth,
221 #[cfg_attr(feature = "visitable", visit(skip))]
222 pub close: Option<T![')']>,
223}
224
225#[derive(Parse, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
226#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
227#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
228pub struct NthLastColPseudoFunction {
229 #[cfg_attr(feature = "visitable", visit(skip))]
230 pub colon: T![:],
231 #[cfg_attr(feature = "visitable", visit(skip))]
232 #[atom(CssAtomSet::NthLastCol)]
233 pub function: T![Function],
234 pub value: Nth,
235 #[cfg_attr(feature = "visitable", visit(skip))]
236 pub close: Option<T![')']>,
237}
238
239#[derive(Parse, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
240#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
241#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
242pub struct NthLastOfTypePseudoFunction {
243 #[cfg_attr(feature = "visitable", visit(skip))]
244 pub colon: T![:],
245 #[cfg_attr(feature = "visitable", visit(skip))]
246 #[atom(CssAtomSet::NthLastOfType)]
247 pub function: T![Function],
248 pub value: Nth,
249 #[cfg_attr(feature = "visitable", visit(skip))]
250 pub close: Option<T![')']>,
251}
252
253#[derive(Parse, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
254#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
255#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
256pub struct NthOfTypePseudoFunction {
257 #[cfg_attr(feature = "visitable", visit(skip))]
258 pub colon: T![:],
259 #[cfg_attr(feature = "visitable", visit(skip))]
260 #[atom(CssAtomSet::NthOfType)]
261 pub function: T![Function],
262 pub value: Nth,
263 #[cfg_attr(feature = "visitable", visit(skip))]
264 pub close: Option<T![')']>,
265}
266
267#[derive(Parse, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
268#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
269#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
270pub struct WherePseudoFunction<'a> {
271 #[cfg_attr(feature = "visitable", visit(skip))]
272 pub colon: T![:],
273 #[cfg_attr(feature = "visitable", visit(skip))]
274 #[atom(CssAtomSet::Where)]
275 pub function: T![Function],
276 pub value: ForgivingSelector<'a>,
277 #[cfg_attr(feature = "visitable", visit(skip))]
278 pub close: Option<T![')']>,
279}
280
281#[derive(Parse, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
282#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
283#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
284pub struct StatePseudoFunction {
285 pub colon: T![:],
286 #[atom(CssAtomSet::State)]
287 pub function: T![Function],
288 pub value: T![Ident],
289 pub close: Option<T![')']>,
290}
291
292#[derive(Parse, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
293#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
294#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
295pub struct HeadingPseudoFunction<'a> {
296 pub colon: T![:],
297 #[atom(CssAtomSet::Heading)]
298 pub function: T![Function],
299 pub value: CommaSeparated<'a, Nth>,
300 pub close: Option<T![')']>,
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306
307 #[test]
308 fn size_test() {
309 assert_eq!(std::mem::size_of::<FunctionalPseudoClass>(), 104);
310 assert_eq!(std::mem::size_of::<DirValue>(), 16);
311 }
312}