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