css_ast/selector/
pseudo_class.rs

1use crate::{CssAtomSet, CssDiagnostic};
2use css_parse::{Cursor, Diagnostic, Parse, Parser, Result as ParserResult, T};
3use csskit_derives::{Peek, SemanticEq, ToCursors, ToSpan};
4
5use super::{moz::MozPseudoClass, ms::MsPseudoClass, o::OPseudoClass, webkit::WebkitPseudoClass};
6
7macro_rules! apply_pseudo_class {
8	($macro: ident) => {
9		$macro! {
10			Active: CssAtomSet::Active,
11			AnyLink: CssAtomSet::AnyLink,
12			Autofill: CssAtomSet::Autofill,
13			Blank: CssAtomSet::Blank,
14			Buffering: CssAtomSet::Buffering,
15			Checked: CssAtomSet::Checked,
16			Current: CssAtomSet::Current,
17			Default: CssAtomSet::Default,
18			Defined: CssAtomSet::Defined,
19			Disabled: CssAtomSet::Disabled,
20			Empty: CssAtomSet::Empty,
21			Enabled: CssAtomSet::Enabled,
22			First: CssAtomSet::First,
23			FirstChild: CssAtomSet::FirstChild,
24			FirstOfType: CssAtomSet::FirstOfType,
25			Focus: CssAtomSet::Focus,
26			FocusVisible: CssAtomSet::FocusVisible,
27			FocusWithin: CssAtomSet::FocusWithin,
28			Fullscreen: CssAtomSet::Fullscreen,
29			Future: CssAtomSet::Future,
30			HasSlotted: CssAtomSet::HasSlotted,
31			Host: CssAtomSet::Host,
32			Heading: CssAtomSet::Heading,
33			Hover: CssAtomSet::Hover,
34			InRange: CssAtomSet::InRange,
35			Indeterminate: CssAtomSet::Indeterminate,
36			Invalid: CssAtomSet::Invalid,
37			LastChild: CssAtomSet::LastChild,
38			LastOfType: CssAtomSet::LastOfType,
39			Left: CssAtomSet::Left,
40			Link: CssAtomSet::Link,
41			LocalLink: CssAtomSet::LocalLink,
42			Modal: CssAtomSet::Modal,
43			Muted: CssAtomSet::Muted,
44			OnlyChild: CssAtomSet::OnlyChild,
45			OnlyOfType: CssAtomSet::OnlyOfType,
46			Open: CssAtomSet::Open,
47			Optional: CssAtomSet::Optional,
48			OutOfRange: CssAtomSet::OutOfRange,
49			Past: CssAtomSet::Past,
50			Paused: CssAtomSet::Paused,
51			PictureInPicture: CssAtomSet::PictureInPicture,
52			PlaceholderShown: CssAtomSet::PlaceholderShown,
53			Playing: CssAtomSet::Playing,
54			PopoverOpen: CssAtomSet::PopoverOpen,
55			ReadOnly: CssAtomSet::ReadOnly,
56			ReadWrite: CssAtomSet::ReadWrite,
57			Required: CssAtomSet::Required,
58			Right: CssAtomSet::Right,
59			Root: CssAtomSet::Root,
60			Scope: CssAtomSet::Scope,
61			Seeking: CssAtomSet::Seeking,
62			Stalled: CssAtomSet::Stalled,
63			Target: CssAtomSet::Target,
64			TargetCurrent: CssAtomSet::TargetCurrent,
65			TargetWithin: CssAtomSet::TargetWithin,
66			UserInvalid: CssAtomSet::UserInvalid,
67			Valid: CssAtomSet::Valid,
68			Visited: CssAtomSet::Visited,
69			VolumeLocked: CssAtomSet::VolumeLocked,
70		}
71	};
72}
73
74macro_rules! define_pseudo_class {
75	( $($(#[$meta:meta])* $ident: ident: $pat: pat $(,)*)+ ) => {
76		#[derive(Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
77		#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
78		#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
79		#[cfg_attr(feature = "css_feature_data", derive(::csskit_derives::ToCSSFeature), css_feature("css.selectors"))]
80		pub enum PseudoClass {
81			$($(#[$meta])* $ident(T![:], T![Ident]),)+
82			Webkit(WebkitPseudoClass),
83			Moz(MozPseudoClass),
84			Ms(MsPseudoClass),
85			O(OPseudoClass),
86		}
87	};
88}
89apply_pseudo_class!(define_pseudo_class);
90
91impl<'a> Parse<'a> for PseudoClass {
92	fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
93	where
94		I: Iterator<Item = Cursor> + Clone,
95	{
96		let c = p.peek_n(2);
97		macro_rules! match_keyword {
98			( $($(#[$meta:meta])* $ident: ident: $pat: pat $(,)*)+ ) => {
99				match p.to_atom::<CssAtomSet>(c) {
100					$($pat => {
101						let colon = p.parse::<T![:]>()?;
102						let ident = p.parse::<T![Ident]>()?;
103						Ok(Self::$ident(colon, ident))
104					})+
105					_ => {
106						if let Ok(psuedo) = p.try_parse::<WebkitPseudoClass>() {
107							return Ok(Self::Webkit(psuedo));
108						}
109						if let Ok(psuedo) = p.try_parse::<MozPseudoClass>() {
110							return Ok(Self::Moz(psuedo));
111						}
112						if let Ok(psuedo) = p.try_parse::<MsPseudoClass>() {
113							return Ok(Self::Ms(psuedo));
114						}
115						if let Ok(psuedo) = p.try_parse::<OPseudoClass>() {
116							return Ok(Self::O(psuedo));
117						}
118						Err(Diagnostic::new(c, Diagnostic::unexpected_pseudo_class))?
119					}
120				}
121			};
122		}
123		apply_pseudo_class!(match_keyword)
124	}
125}
126
127#[cfg(test)]
128mod tests {
129	use super::*;
130	use crate::CssAtomSet;
131	use css_parse::assert_parse;
132
133	#[test]
134	fn size_test() {
135		assert_eq!(std::mem::size_of::<PseudoClass>(), 32);
136	}
137
138	#[test]
139	fn test_writes() {
140		assert_parse!(CssAtomSet::ATOMS, PseudoClass, ":target");
141		assert_parse!(CssAtomSet::ATOMS, PseudoClass, ":scope");
142		assert_parse!(CssAtomSet::ATOMS, PseudoClass, ":valid");
143	}
144
145	#[cfg(feature = "css_feature_data")]
146	#[test]
147	fn test_feature_data() {
148		use crate::assert_feature_id;
149		assert_feature_id!(":hover", PseudoClass, "css.selectors.hover");
150		assert_feature_id!(":future", PseudoClass, "css.selectors.future");
151		assert_feature_id!(":volume-locked", PseudoClass, "css.selectors.volume-locked");
152	}
153}