Skip to main content

css_parse/macros/
pseudo_element.rs

1/// A macro for defining pseudo elements.
2///
3/// This makes it much easier to define a pseudo element. Parsing is also a little bit delicate, as the two
4/// [Cursors][crate::Cursor] must appear next to each other - no whitespace nor comments can be present betwixt the
5/// colon and ident.
6///
7/// # Example
8///
9/// ```
10/// use css_parse::*;
11/// use csskit_derives::*;
12/// use derive_atom_set::*;
13///
14/// #[derive(Debug, Default, AtomSet, Copy, Clone, PartialEq)]
15/// pub enum MyAtomSet {
16///   #[default]
17///   _None,
18///   Foo,
19///   Bar,
20///   Baz,
21/// }
22/// impl MyAtomSet {
23///   const ATOMS: MyAtomSet = MyAtomSet::_None;
24/// }
25///
26/// pseudo_element!(
27///   /// Some docs on this type...
28///   #[derive(Debug, ToCursors, ToSpan)]
29///   pub enum MyPseudoElement {
30///     Foo: MyAtomSet::Foo,
31///     Bar: MyAtomSet::Bar,
32///     Baz: MyAtomSet::Baz,
33///   }
34/// );
35///
36/// // Matches are case insensitive
37/// assert_parse!(MyAtomSet::ATOMS, MyPseudoElement, "::FoO");
38///
39/// // The result will be one of the variants in the enum, matching the keyword.
40/// assert_parse!(MyAtomSet::ATOMS, MyPseudoElement, "::bar");
41///
42/// // Words that do not match will fail to Peek.
43/// assert_peek_false!(MyAtomSet::ATOMS, MyPseudoElement, "::bing");
44/// ```
45#[macro_export]
46macro_rules! pseudo_element {
47	($(#[$meta:meta])* $vis:vis enum $name: ident { $first_variant: ident: $atoms: ident::$first:ident, $( $variant: ident: $variant_pat: pat$(,)?)* }) => {
48		$(#[$meta])*
49		$vis enum $name {
50			$first_variant($crate::T![::], $crate::T![Ident]),
51			$($variant($crate::T![::], $crate::T![Ident]),)*
52		}
53
54		impl<'a> $crate::Peek<'a> for $name {
55			const PEEK_KINDSET: $crate::KindSet = $crate::KindSet::new(&[$crate::Kind::Colon]);
56
57			#[inline(always)]
58			fn peek<I>(p: &$crate::Parser<'a, I>, c: $crate::Cursor) -> bool
59			where
60				I: Iterator<Item = $crate::Cursor> + Clone,
61			{
62				let c2 = p.peek_n_including_whitespace(2);
63				let c3 = p.peek_n_including_whitespace(3);
64				c == $crate::Kind::Colon
65				&& c2 == $crate::Kind::Colon
66				&& c3 == $crate::Kind::Ident
67				&& matches!(p.to_atom::<$atoms>(c3), $atoms::$first $(| $variant_pat)*)
68			}
69		}
70
71		impl<'a> $crate::Parse<'a> for $name {
72			fn parse<I>(p: &mut $crate::Parser<'a, I>) -> $crate::Result<Self>
73			where
74				I: Iterator<Item = $crate::Cursor> + Clone,
75			{
76				let colons = p.parse::<$crate::T![::]>()?;
77				let skip = p.set_skip($crate::KindSet::NONE);
78				let ident = p.parse::<$crate::T![Ident]>();
79				p.set_skip(skip);
80				let ident = ident?;
81				match p.to_atom::<$atoms>(ident.into()) {
82					$atoms::$first => Ok(Self::$first_variant(colons, ident)),
83					$($variant_pat => Ok(Self::$variant(colons, ident)),)*
84					_ => {
85						use $crate::ToSpan;
86						Err($crate::Diagnostic::new(ident.into(), Diagnostic::unexpected_ident))?
87					}
88				}
89			}
90		}
91	}
92}