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}