css_ast/selector/
combinator.rs

1use css_parse::{Parse, Parser, Result as ParserResult, T};
2use csskit_derives::{ToCursors, ToSpan, Visitable};
3
4// https://drafts.csswg.org/selectors/#combinators
5#[derive(ToSpan, ToCursors, Visitable, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
6#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(rename_all = "kebab-case"))]
7#[visit(self)]
8pub enum Combinator {
9	Child(T![>]),
10	NextSibling(T![+]),
11	SubsequentSibling(T![~]),
12	Column(T![||]),
13	Nesting(T![&]),
14	Descendant(T![' ']),
15}
16
17impl<'a> Parse<'a> for Combinator {
18	fn parse(p: &mut Parser<'a>) -> ParserResult<Self> {
19		if p.peek::<T![>]>() {
20			Ok(Self::Child(p.parse::<T![>]>()?))
21		} else if p.peek::<T![+]>() {
22			Ok(Self::NextSibling(p.parse::<T![+]>()?))
23		} else if p.peek::<T![~]>() {
24			Ok(Self::SubsequentSibling(p.parse::<T![~]>()?))
25		} else if p.peek::<T![&]>() {
26			Ok(Self::Nesting(p.parse::<T![&]>()?))
27		} else if p.peek::<T![||]>() {
28			Ok(Self::Column(p.parse::<T![||]>()?))
29		} else {
30			Ok(Self::Descendant(p.parse::<T![' ']>()?))
31		}
32	}
33}
34
35#[cfg(test)]
36mod tests {
37	use super::*;
38	use css_parse::assert_parse;
39
40	#[test]
41	fn size_test() {
42		assert_eq!(std::mem::size_of::<Combinator>(), 28);
43	}
44
45	#[test]
46	fn test_writes() {
47		assert_parse!(Combinator, ">");
48		assert_parse!(Combinator, "+");
49		assert_parse!(Combinator, "~");
50		assert_parse!(Combinator, "&");
51		// Descendent combinator
52		assert_parse!(Combinator, "     ");
53		assert_parse!(Combinator, "     ");
54		assert_parse!(Combinator, "  /**/   /**/   /**/ ", "  ");
55		// Column
56		assert_parse!(Combinator, "||");
57	}
58}