1use crate::{
2 AssociatedWhitespaceRules, Cursor, CursorSink, Diagnostic, FunctionBlock, Kind, KindSet, Parse, Parser, Peek,
3 Result as ParserResult, SemanticEq, SimpleBlock, Span, State, T, ToCursors, ToSpan,
4};
5
6#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(untagged))]
12pub enum ComponentValue<'a> {
13 SimpleBlock(SimpleBlock<'a>),
14 Function(FunctionBlock<'a>),
15 Whitespace(T![Whitespace]),
16 Number(T![Number]),
17 Dimension(T![Dimension]),
18 Ident(T![Ident]),
19 AtKeyword(T![AtKeyword]),
20 Hash(T![Hash]),
21 String(T![String]),
22 Url(T![Url]),
23 Delim(T![Delim]),
24 Colon(T![:]),
25 Semicolon(T![;]),
26 Comma(T![,]),
27}
28
29impl<'a> Peek<'a> for ComponentValue<'a> {
30 const PEEK_KINDSET: KindSet = KindSet::new(&[
31 Kind::Whitespace,
32 Kind::Number,
33 Kind::Dimension,
34 Kind::Ident,
35 Kind::AtKeyword,
36 Kind::Hash,
37 Kind::String,
38 Kind::Url,
39 Kind::Delim,
40 Kind::Colon,
41 Kind::Semicolon,
42 Kind::Comma,
43 Kind::Function,
44 Kind::LeftCurly,
45 Kind::LeftParen,
46 Kind::LeftSquare,
47 ]);
48 #[inline(always)]
49 fn peek<Iter>(p: &Parser<'a, Iter>, c: Cursor) -> bool
50 where
51 Iter: Iterator<Item = Cursor> + Clone,
52 {
53 c == Self::PEEK_KINDSET || <T![' ']>::peek(p, c)
54 }
55}
56
57impl<'a> Parse<'a> for ComponentValue<'a> {
59 fn parse<Iter>(p: &mut Parser<'a, Iter>) -> ParserResult<Self>
60 where
61 Iter: Iterator<Item = Cursor> + Clone,
62 {
63 let c = p.peek_n(1);
64 Ok(if <T![' ']>::peek(p, c) {
65 Self::Whitespace(p.parse::<T![' ']>()?)
66 } else if <T![PairWiseStart]>::peek(p, c) {
67 let old_state = p.set_state(State::Nested);
68 let block = p.parse::<SimpleBlock>();
69 p.set_state(old_state);
70 Self::SimpleBlock(block?)
71 } else if <T![Function]>::peek(p, c) {
72 Self::Function(p.parse::<FunctionBlock>()?)
73 } else if <T![Number]>::peek(p, c) {
74 Self::Number(p.parse::<T![Number]>()?)
75 } else if <T![Dimension]>::peek(p, c) {
76 Self::Dimension(p.parse::<T![Dimension]>()?)
77 } else if <T![Ident]>::peek(p, c) {
78 Self::Ident(p.parse::<T![Ident]>()?)
79 } else if <T![AtKeyword]>::peek(p, c) {
80 Self::AtKeyword(p.parse::<T![AtKeyword]>()?)
81 } else if <T![Hash]>::peek(p, c) {
82 Self::Hash(p.parse::<T![Hash]>()?)
83 } else if <T![String]>::peek(p, c) {
84 Self::String(p.parse::<T![String]>()?)
85 } else if <T![Url]>::peek(p, c) {
86 Self::Url(p.parse::<T![Url]>()?)
87 } else if <T![Delim]>::peek(p, c) {
88 p.parse::<T![Delim]>().map(|delim| {
89 let mut rules = AssociatedWhitespaceRules::none();
91 if p.peek_n_with_skip(1, KindSet::COMMENTS) == Kind::Whitespace {
92 rules |= AssociatedWhitespaceRules::EnforceAfter;
93 } else {
94 rules |= AssociatedWhitespaceRules::BanAfter;
95 }
96 Self::Delim(delim.with_associated_whitespace(rules))
97 })?
98 } else if <T![:]>::peek(p, c) {
99 Self::Colon(p.parse::<T![:]>()?)
100 } else if <T![;]>::peek(p, c) {
101 Self::Semicolon(p.parse::<T![;]>()?)
102 } else if <T![,]>::peek(p, c) {
103 Self::Comma(p.parse::<T![,]>()?)
104 } else {
105 Err(Diagnostic::new(p.next(), Diagnostic::unexpected))?
106 })
107 }
108}
109
110impl<'a> ToCursors for ComponentValue<'a> {
111 fn to_cursors(&self, s: &mut impl CursorSink) {
112 match self {
113 Self::SimpleBlock(t) => ToCursors::to_cursors(t, s),
114 Self::Function(t) => ToCursors::to_cursors(t, s),
115 Self::Ident(t) => ToCursors::to_cursors(t, s),
116 Self::AtKeyword(t) => ToCursors::to_cursors(t, s),
117 Self::Hash(t) => ToCursors::to_cursors(t, s),
118 Self::String(t) => ToCursors::to_cursors(t, s),
119 Self::Url(t) => ToCursors::to_cursors(t, s),
120 Self::Delim(t) => ToCursors::to_cursors(t, s),
121 Self::Number(t) => ToCursors::to_cursors(t, s),
122 Self::Dimension(t) => ToCursors::to_cursors(t, s),
123 Self::Whitespace(t) => ToCursors::to_cursors(t, s),
124 Self::Colon(t) => ToCursors::to_cursors(t, s),
125 Self::Semicolon(t) => ToCursors::to_cursors(t, s),
126 Self::Comma(t) => ToCursors::to_cursors(t, s),
127 }
128 }
129}
130
131impl<'a> ToSpan for ComponentValue<'a> {
132 fn to_span(&self) -> Span {
133 match self {
134 Self::SimpleBlock(t) => t.to_span(),
135 Self::Function(t) => t.to_span(),
136 Self::Ident(t) => t.to_span(),
137 Self::AtKeyword(t) => t.to_span(),
138 Self::Hash(t) => t.to_span(),
139 Self::String(t) => t.to_span(),
140 Self::Url(t) => t.to_span(),
141 Self::Delim(t) => t.to_span(),
142 Self::Number(t) => t.to_span(),
143 Self::Dimension(t) => t.to_span(),
144 Self::Whitespace(t) => t.to_span(),
145 Self::Colon(t) => t.to_span(),
146 Self::Semicolon(t) => t.to_span(),
147 Self::Comma(t) => t.to_span(),
148 }
149 }
150}
151
152impl<'a> SemanticEq for ComponentValue<'a> {
153 fn semantic_eq(&self, other: &Self) -> bool {
154 match (self, other) {
155 (Self::SimpleBlock(a), Self::SimpleBlock(b)) => a.semantic_eq(b),
156 (Self::Function(a), Self::Function(b)) => a.semantic_eq(b),
157 (Self::Number(a), Self::Number(b)) => a.semantic_eq(b),
158 (Self::Dimension(a), Self::Dimension(b)) => a.semantic_eq(b),
159 (Self::Ident(a), Self::Ident(b)) => a.semantic_eq(b),
160 (Self::AtKeyword(a), Self::AtKeyword(b)) => a.semantic_eq(b),
161 (Self::Hash(a), Self::Hash(b)) => a.semantic_eq(b),
162 (Self::String(a), Self::String(b)) => a.semantic_eq(b),
163 (Self::Url(a), Self::Url(b)) => a.semantic_eq(b),
164 (Self::Delim(a), Self::Delim(b)) => a.semantic_eq(b),
165 (Self::Colon(a), Self::Colon(b)) => a.semantic_eq(b),
166 (Self::Semicolon(a), Self::Semicolon(b)) => a.semantic_eq(b),
167 (Self::Comma(a), Self::Comma(b)) => a.semantic_eq(b),
168 (Self::Whitespace(_), Self::Whitespace(_)) => true,
170 _ => false, }
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178 use crate::{EmptyAtomSet, test_helpers::*};
179
180 #[test]
181 fn size_test() {
182 assert_eq!(std::mem::size_of::<ComponentValue>(), 64);
183 }
184
185 #[test]
186 fn test_writes() {
187 assert_parse!(EmptyAtomSet::ATOMS, ComponentValue, "foo");
188 assert_parse!(EmptyAtomSet::ATOMS, ComponentValue, " ");
189 assert_parse!(EmptyAtomSet::ATOMS, ComponentValue, "{block}");
190 }
191}