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/// use bumpalo::Bump;
14///
15/// #[derive(Debug, Default, AtomSet, Copy, Clone, PartialEq)]
16/// pub enum MyAtomSet {
17///   #[default]
18///   _None,
19///   Foo,
20///   Bar,
21///   Baz,
22/// }
23/// impl MyAtomSet {
24///   const ATOMS: MyAtomSet = MyAtomSet::_None;
25/// }
26///
27/// pseudo_element!(
28///   /// Some docs on this type...
29///   #[derive(Debug, ToCursors, ToSpan)]
30///   pub enum MyPseudoElement {
31///     Foo: MyAtomSet::Foo,
32///     Bar: MyAtomSet::Bar,
33///     Baz: MyAtomSet::Baz,
34///   }
35/// );
36///
37/// // Matches are case insensitive
38/// assert_parse!(MyAtomSet::ATOMS, MyPseudoElement, "::FoO");
39///
40/// // The result will be one of the variants in the enum, matching the keyword.
41/// assert_parse!(MyAtomSet::ATOMS, MyPseudoElement, "::bar");
42///
43/// // Words that do not match will fail to parse.
44/// assert_parse_error!(MyAtomSet::ATOMS, MyPseudoElement, "::bing");
45/// ```
46#[macro_export]
47macro_rules! pseudo_element {
48	($(#[$meta:meta])* $vis:vis enum $name: ident { $first_variant: ident: $atoms: ident::$first:ident, $( $variant: ident: $variant_pat: pat$(,)?)* }) => {
49		$(#[$meta])*
50		$vis enum $name {
51			$first_variant($crate::T![::], $crate::T![Ident]),
52			$($variant($crate::T![::], $crate::T![Ident]),)*
53		}
54
55		impl<'a> $crate::Peek<'a> for $name {
56			fn peek<I>(p: &$crate::Parser<'a, I>, c: $crate::Cursor) -> bool
57			where
58				I: Iterator<Item = $crate::Cursor> + Clone,
59			{
60				let c2 = p.peek_n(2);
61				let c3 = p.peek_n(3);
62				c == $crate::Kind::Colon
63				&& c2 == $crate::Kind::Colon
64				&& c3 == $crate::Kind::Ident
65				&& matches!(p.to_atom::<$atoms>(c3), $atoms::$first $(| $variant_pat)*)
66			}
67		}
68
69		impl<'a> $crate::Parse<'a> for $name {
70			fn parse<I>(p: &mut $crate::Parser<'a, I>) -> $crate::Result<Self>
71			where
72				I: Iterator<Item = $crate::Cursor> + Clone,
73			{
74				let colons = p.parse::<$crate::T![::]>()?;
75				let skip = p.set_skip($crate::KindSet::NONE);
76				let ident = p.parse::<$crate::T![Ident]>();
77				p.set_skip(skip);
78				let ident = ident?;
79				match p.to_atom::<$atoms>(ident.into()) {
80					$atoms::$first => Ok(Self::$first_variant(colons, ident)),
81					$($variant_pat => Ok(Self::$variant(colons, ident)),)*
82					_ => {
83						use $crate::ToSpan;
84						Err($crate::Diagnostic::new(ident.into(), Diagnostic::unexpected_ident))?
85					}
86				}
87			}
88		}
89	}
90}