css_ast/selector/
functional_pseudo_class.rs

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}