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}