Skip to main content

css_parse/syntax/
either.rs

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