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#[derive(csskit_derives::NodeWithMetadata)]
80		#[cfg_attr(feature = "css_feature_data", derive(::csskit_derives::ToCSSFeature), css_feature("css.selectors"))]
81		pub enum PseudoClass {
82			$($(#[$meta])* $ident(T![:], T![Ident]),)+
83			Webkit(WebkitPseudoClass),
84			Moz(MozPseudoClass),
85			Ms(MsPseudoClass),
86			O(OPseudoClass),
87		}
88	};
89}
90apply_pseudo_class!(define_pseudo_class);
91
92impl<'a> Parse<'a> for PseudoClass {
93	fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
94	where
95		I: Iterator<Item = Cursor> + Clone,
96	{
97		let c = p.peek_n(2);
98		macro_rules! match_keyword {
99			( $($(#[$meta:meta])* $ident: ident: $pat: pat $(,)*)+ ) => {
100				match p.to_atom::<CssAtomSet>(c) {
101					$($pat => {
102						let colon = p.parse::<T![:]>()?;
103						let ident = p.parse::<T![Ident]>()?;
104						Ok(Self::$ident(colon, ident))
105					})+
106					_ => {
107						if let Ok(psuedo) = p.try_parse::<WebkitPseudoClass>() {
108							return Ok(Self::Webkit(psuedo));
109						}
110						if let Ok(psuedo) = p.try_parse::<MozPseudoClass>() {
111							return Ok(Self::Moz(psuedo));
112						}
113						if let Ok(psuedo) = p.try_parse::<MsPseudoClass>() {
114							return Ok(Self::Ms(psuedo));
115						}
116						if let Ok(psuedo) = p.try_parse::<OPseudoClass>() {
117							return Ok(Self::O(psuedo));
118						}
119						Err(Diagnostic::new(c, Diagnostic::unexpected_pseudo_class))?
120					}
121				}
122			};
123		}
124		apply_pseudo_class!(match_keyword)
125	}
126}
127
128#[cfg(test)]
129mod tests {
130	use super::*;
131	use crate::CssAtomSet;
132	use css_parse::assert_parse;
133
134	#[test]
135	fn size_test() {
136		assert_eq!(std::mem::size_of::<PseudoClass>(), 32);
137	}
138
139	#[test]
140	fn test_writes() {
141		assert_parse!(CssAtomSet::ATOMS, PseudoClass, ":target");
142		assert_parse!(CssAtomSet::ATOMS, PseudoClass, ":scope");
143		assert_parse!(CssAtomSet::ATOMS, PseudoClass, ":valid");
144	}
145
146	#[cfg(feature = "css_feature_data")]
147	#[test]
148	fn test_feature_data() {
149		use crate::assert_feature_id;
150		assert_feature_id!(":hover", PseudoClass, "css.selectors.hover");
151		assert_feature_id!(":future", PseudoClass, "css.selectors.future");
152		assert_feature_id!(":volume-locked", PseudoClass, "css.selectors.volume-locked");
153	}
154}