Skip to main content

css_parse/syntax/
either.rs

1use crate::{NodeMetadata, NodeWithMetadata, Parse, Peek, SemanticEq, ToCursors, ToNumberValue};
2use css_lexer::{Cursor, KindSet, ToSpan};
3
4#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
6pub enum Either<Left, Right> {
7	Left(Left),
8	Right(Right),
9}
10
11impl<'a, Left: Peek<'a>, Right: Peek<'a>> Peek<'a> for Either<Left, Right> {
12	const PEEK_KINDSET: KindSet = Left::PEEK_KINDSET.combine(Right::PEEK_KINDSET);
13
14	fn peek<I>(p: &crate::Parser<'a, I>, c: Cursor) -> bool
15	where
16		I: Iterator<Item = Cursor> + Clone,
17	{
18		Left::peek(p, c) || Right::peek(p, c)
19	}
20}
21
22impl<'a, Left: Parse<'a> + Peek<'a>, Right: Parse<'a>> Parse<'a> for Either<Left, Right> {
23	fn parse<I>(p: &mut crate::Parser<'a, I>) -> crate::Result<Self>
24	where
25		I: Iterator<Item = Cursor> + Clone,
26	{
27		let c = p.peek_n(1);
28		if Left::peek(p, c)
29			&& let Ok(res) = Left::parse(p)
30		{
31			Ok(Either::Left(res))
32		} else {
33			Right::parse(p).map(Either::Right)
34		}
35	}
36}
37
38impl<Left: ToCursors, Right: ToCursors> ToCursors for Either<Left, Right> {
39	fn to_cursors(&self, s: &mut impl crate::CursorSink) {
40		match self {
41			Self::Left(t) => t.to_cursors(s),
42			Self::Right(t) => t.to_cursors(s),
43		}
44	}
45}
46
47impl<Left: ToSpan, Right: ToSpan> ToSpan for Either<Left, Right> {
48	fn to_span(&self) -> css_lexer::Span {
49		match self {
50			Self::Left(t) => t.to_span(),
51			Self::Right(t) => t.to_span(),
52		}
53	}
54}
55
56impl<Left, Right, M: NodeMetadata> NodeWithMetadata<M> for Either<Left, Right>
57where
58	Left: NodeWithMetadata<M>,
59	Right: NodeWithMetadata<M>,
60{
61	fn metadata(&self) -> M {
62		match self {
63			Self::Left(t) => t.metadata(),
64			Self::Right(t) => t.metadata(),
65		}
66	}
67}
68
69impl<Left, Right> ToNumberValue for Either<Left, Right>
70where
71	Left: ToNumberValue,
72	Right: ToNumberValue,
73{
74	fn to_number_value(&self) -> Option<f32> {
75		match self {
76			Self::Left(t) => t.to_number_value(),
77			Self::Right(t) => t.to_number_value(),
78		}
79	}
80}
81
82impl<Left: SemanticEq, Right: SemanticEq> SemanticEq for Either<Left, Right> {
83	fn semantic_eq(&self, other: &Self) -> bool {
84		match (self, other) {
85			(Self::Left(a), Self::Left(b)) => a.semantic_eq(b),
86			(Self::Right(a), Self::Right(b)) => a.semantic_eq(b),
87			_ => false,
88		}
89	}
90}
91
92impl<Left: Copy, Right: Copy> Copy for Either<Left, Right> {}
93
94impl<Left, Right> From<Either<Left, Right>> for Cursor
95where
96	Left: Copy,
97	Right: Copy,
98	Cursor: From<Left> + From<Right>,
99{
100	fn from(value: Either<Left, Right>) -> Self {
101		match value {
102			Either::Left(t) => t.into(),
103			Either::Right(t) => t.into(),
104		}
105	}
106}
107
108#[cfg(test)]
109mod tests {
110	use super::*;
111	use crate::{Parser, T, assert_parse, assert_parse_error};
112	use bumpalo::Bump;
113	use css_lexer::{EmptyAtomSet, Lexer};
114
115	type IdentOrNumber = Either<T![Ident], T![Number]>;
116	type NumberOrDimension = Either<T![Number], T![Dimension]>;
117
118	#[test]
119	fn size_test() {
120		assert_eq!(std::mem::size_of::<IdentOrNumber>(), 16);
121	}
122
123	#[test]
124	fn test_writes() {
125		assert_parse!(EmptyAtomSet::ATOMS, IdentOrNumber, "all", Either::Left(_));
126		assert_parse!(EmptyAtomSet::ATOMS, IdentOrNumber, "1", Either::Right(_));
127	}
128
129	#[test]
130	fn test_errors() {
131		assert_parse_error!(EmptyAtomSet::ATOMS, IdentOrNumber, "");
132		assert_parse_error!(EmptyAtomSet::ATOMS, IdentOrNumber, "foo(");
133		assert_parse_error!(EmptyAtomSet::ATOMS, IdentOrNumber, "auto auto");
134		assert_parse_error!(EmptyAtomSet::ATOMS, IdentOrNumber, "1 1");
135	}
136
137	#[test]
138	fn test_to_number_value() {
139		let bump = Bump::default();
140		let source_text = "47";
141		let lexer = Lexer::new(&EmptyAtomSet::ATOMS, source_text);
142		let mut p = Parser::new(&bump, source_text, lexer);
143		let num = p.parse_entirely::<NumberOrDimension>().output.unwrap();
144		assert_eq!(num.to_number_value(), Some(47.0));
145
146		let source_text = "47px";
147		let lexer = Lexer::new(&EmptyAtomSet::ATOMS, source_text);
148		let mut p = Parser::new(&bump, source_text, lexer);
149		let num = p.parse_entirely::<NumberOrDimension>().output.unwrap();
150		assert_eq!(num.to_number_value(), Some(47.0));
151	}
152}