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