css_ast/selector/
functional_pseudo_class.rs

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}