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, which would otherwise need to define a
4/// [keyword_set][crate::keyword_set] or similar, in order to build up the two [Cursors][crate::Cursor] required to
5/// parse. Parsing is also a little bit delicate, as the two [Cursors][crate::Cursor] must appear next to each
6/// other - no whitespace nor comments can be present betwixt the colon and ident.
7///
8/// # Example
9///
10/// ```
11/// use css_parse::*;
12/// use bumpalo::Bump;
13/// pseudo_element!(
14///   /// Some docs on this type...
15///   pub enum MyPseudoElement {
16///     Foo: "foo",
17///     Bar: "bar",
18///     Baz: "baz"
19///   }
20/// );
21///
22/// // Matches are case insensitive
23/// assert_parse!(MyPseudoElement, "::FoO");
24///
25/// // The result will be one of the variants in the enum, matching the keyword.
26/// assert_parse!(MyPseudoElement, "::bar");
27///
28/// // Words that do not match will fail to parse.
29/// assert_parse_error!(MyPseudoElement, "::bing");
30/// ```
31#[macro_export]
32macro_rules! pseudo_element {
33	($(#[$meta:meta])* $vis:vis enum $name: ident { $( $variant: ident: $variant_str: tt$(,)?)+ }) => {
34		$(#[$meta])*
35		#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
36		#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
37		$vis enum $name {
38			$($variant($crate::T![::], $crate::T![Ident]),)+
39		}
40
41		impl<'a> $crate::Peek<'a> for $name {
42			fn peek(p: &$crate::Parser<'a>, c: $crate::Cursor) -> bool {
43				let c2 = p.peek_n(2);
44				let c3 = p.peek_n(3);
45				c == $crate::Kind::Colon
46				&& c2 == $crate::Kind::Colon
47				&& c3 == $crate::Kind::Ident
48				&& Self::MAP.get(&p.parse_str_lower(c3)).is_some()
49			}
50		}
51
52		impl<'a> $crate::Parse<'a> for $name {
53			fn parse(p: &mut $crate::Parser<'a>) -> $crate::Result<Self> {
54				let colons = p.parse::<$crate::T![::]>()?;
55				let skip = p.set_skip($crate::KindSet::NONE);
56				let ident = p.parse::<$crate::T![Ident]>();
57				p.set_skip(skip);
58				let ident = ident?;
59				if let Some(val) = Self::MAP.get(&p.parse_str_lower(ident.into())) {
60					match val {
61						$(Self::$variant(_, _) => Ok(Self::$variant(colons, ident)),)+
62					}
63				} else {
64					use $crate::ToSpan;
65					Err($crate::diagnostics::UnexpectedIdent(p.parse_str(ident.into()).into(), ident.into()))?
66				}
67			}
68		}
69
70		impl $crate::ToCursors for $name {
71			fn to_cursors(&self, s: &mut impl $crate::CursorSink) {
72				match self {
73					$(Self::$variant(colons, ident) => {
74						$crate::ToCursors::to_cursors(colons, s);
75						s.append((*ident).into());
76					})+
77				}
78			}
79		}
80
81		impl $crate::ToSpan for $name {
82			fn to_span(&self) -> $crate::Span {
83				match self {
84					$($name::$variant(a, b) => a.to_span() + b.to_span(),)+
85				}
86			}
87		}
88
89		impl $name {
90			const MAP: phf::Map<&'static str, $name> = phf::phf_map! {
91					$($variant_str => $name::$variant(<$crate::T![::]>::dummy(), <$crate::T![Ident]>::dummy()),)+
92			};
93		}
94	}
95}