Skip to main content

css_ast/selector/
pseudo_class.rs

1use super::prelude::*;
2
3use super::{moz::MozPseudoClass, ms::MsPseudoClass, o::OPseudoClass, webkit::WebkitPseudoClass};
4
5macro_rules! apply_pseudo_class {
6	($macro: ident) => {
7		$macro! {
8			Active: CssAtomSet::Active,
9			ActiveViewTransition: CssAtomSet::ActiveViewTransition,
10			AnimatedImage: CssAtomSet::AnimatedImage,
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			FirstOfPage: CssAtomSet::FirstOfPage,
25			FirstOfType: CssAtomSet::FirstOfType,
26			Focus: CssAtomSet::Focus,
27			FocusVisible: CssAtomSet::FocusVisible,
28			FocusWithin: CssAtomSet::FocusWithin,
29			Fullscreen: CssAtomSet::Fullscreen,
30			Future: CssAtomSet::Future,
31			HasSlotted: CssAtomSet::HasSlotted,
32			Heading: CssAtomSet::Heading,
33			HighValue: CssAtomSet::HighValue,
34			Host: CssAtomSet::Host,
35			Hover: CssAtomSet::Hover,
36			InRange: CssAtomSet::InRange,
37			Indeterminate: CssAtomSet::Indeterminate,
38			InterestSource: CssAtomSet::InterestSource,
39			InterestTarget: CssAtomSet::InterestTarget,
40			Invalid: CssAtomSet::Invalid,
41			LastChild: CssAtomSet::LastChild,
42			LastOfPage: CssAtomSet::LastOfPage,
43			LastOfType: CssAtomSet::LastOfType,
44			Left: CssAtomSet::Left,
45			Link: CssAtomSet::Link,
46			LocalLink: CssAtomSet::LocalLink,
47			LowValue: CssAtomSet::LowValue,
48			Modal: CssAtomSet::Modal,
49			Muted: CssAtomSet::Muted,
50			NavSource: CssAtomSet::NavSource,
51			OnlyChild: CssAtomSet::OnlyChild,
52			OnlyOfType: CssAtomSet::OnlyOfType,
53			Open: CssAtomSet::Open,
54			OptimalValue: CssAtomSet::OptimalValue,
55			Optional: CssAtomSet::Optional,
56			OutOfRange: CssAtomSet::OutOfRange,
57			Past: CssAtomSet::Past,
58			Paused: CssAtomSet::Paused,
59			PictureInPicture: CssAtomSet::PictureInPicture,
60			PlaceholderShown: CssAtomSet::PlaceholderShown,
61			Playing: CssAtomSet::Playing,
62			PopoverOpen: CssAtomSet::PopoverOpen,
63			ReadOnly: CssAtomSet::ReadOnly,
64			ReadWrite: CssAtomSet::ReadWrite,
65			Required: CssAtomSet::Required,
66			Right: CssAtomSet::Right,
67			Root: CssAtomSet::Root,
68			Scope: CssAtomSet::Scope,
69			Seeking: CssAtomSet::Seeking,
70			Snapped: CssAtomSet::Snapped,
71			SnappedBlock: CssAtomSet::SnappedBlock,
72			SnappedInline: CssAtomSet::SnappedInline,
73			SnappedX: CssAtomSet::SnappedX,
74			SnappedY: CssAtomSet::SnappedY,
75			Stalled: CssAtomSet::Stalled,
76			Target: CssAtomSet::Target,
77			TargetAfter: CssAtomSet::TargetAfter,
78			TargetBefore: CssAtomSet::TargetBefore,
79			TargetCurrent: CssAtomSet::TargetCurrent,
80			Unchecked: CssAtomSet::Unchecked,
81			UserInvalid: CssAtomSet::UserInvalid,
82			UserValid: CssAtomSet::UserValid,
83			Valid: CssAtomSet::Valid,
84			Visited: CssAtomSet::Visited,
85			VolumeLocked: CssAtomSet::VolumeLocked,
86		}
87	};
88}
89
90macro_rules! define_pseudo_class {
91	( $($(#[$meta:meta])* $ident: ident: $pat: pat $(,)*)+ ) => {
92		#[node]
93		#[derive(Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
94		#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
95		#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
96#[derive(csskit_derives::NodeWithMetadata)]
97		#[cfg_attr(feature = "css_feature_data", derive(::csskit_derives::ToCSSFeature), css_feature("css.selectors"))]
98		pub enum PseudoClass {
99			$($(#[$meta])* $ident(#[semantic_eq(skip)] T![:], T![Ident]),)+
100			Webkit(WebkitPseudoClass),
101			Moz(MozPseudoClass),
102			Ms(MsPseudoClass),
103			O(OPseudoClass),
104		}
105	};
106}
107apply_pseudo_class!(define_pseudo_class);
108
109impl<'a> Parse<'a> for PseudoClass {
110	fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
111	where
112		I: Iterator<Item = Cursor> + Clone,
113	{
114		let c = p.peek_n(2);
115		macro_rules! match_keyword {
116			( $($(#[$meta:meta])* $ident: ident: $pat: pat $(,)*)+ ) => {
117				match p.to_atom::<CssAtomSet>(c) {
118					$($pat => {
119						let colon = p.parse::<T![:]>()?;
120						let ident = p.parse::<T![Ident]>()?;
121						Ok(Self::$ident(colon, ident))
122					})+
123					_ => {
124						if let Ok(psuedo) = p.try_parse::<WebkitPseudoClass>() {
125							return Ok(Self::Webkit(psuedo));
126						}
127						if let Ok(psuedo) = p.try_parse::<MozPseudoClass>() {
128							return Ok(Self::Moz(psuedo));
129						}
130						if let Ok(psuedo) = p.try_parse::<MsPseudoClass>() {
131							return Ok(Self::Ms(psuedo));
132						}
133						if let Ok(psuedo) = p.try_parse::<OPseudoClass>() {
134							return Ok(Self::O(psuedo));
135						}
136						Err(Diagnostic::new(c, Diagnostic::unexpected_pseudo_class))?
137					}
138				}
139			};
140		}
141		apply_pseudo_class!(match_keyword)
142	}
143}
144
145#[cfg(test)]
146mod tests {
147	use super::*;
148	use crate::CssAtomSet;
149	use css_parse::assert_parse;
150
151	#[test]
152	fn test_writes() {
153		assert_parse!(CssAtomSet::ATOMS, PseudoClass, ":target");
154		assert_parse!(CssAtomSet::ATOMS, PseudoClass, ":scope");
155		assert_parse!(CssAtomSet::ATOMS, PseudoClass, ":valid");
156	}
157
158	#[test]
159	fn unknown_pseudo_class_names_only_the_bad_token() {
160		let source = ":nonsense";
161		let arena = css_parse::Arena::default();
162		let lexer = css_lexer::Lexer::new(&CssAtomSet::ATOMS, source);
163		let result = css_parse::Parser::new(&arena, source, lexer).parse_entirely::<PseudoClass>();
164		let error = result.errors.first().expect("a diagnostic");
165		let meta = error.meta(source);
166		assert_eq!(meta.message, "Unexpected pseudo selector ':nonsense'");
167	}
168
169	#[cfg(feature = "css_feature_data")]
170	#[test]
171	fn test_feature_data() {
172		use crate::assert_feature_id;
173		assert_feature_id!(":hover", PseudoClass, "css.selectors.hover");
174		assert_feature_id!(":future", PseudoClass, "css.selectors.future");
175		assert_feature_id!(":volume-locked", PseudoClass, "css.selectors.volume-locked");
176	}
177}