Skip to main content

css_parse/syntax/
either.rs

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