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}