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}