1use crate::{
2 AssociatedWhitespaceRules, Cursor, CursorSink, Diagnostic, FunctionBlock, Kind, KindSet, Parse, Parser, Peek,
3 Result as ParserResult, SimpleBlock, Span, State, T, ToCursors, ToSpan,
4};
5
6#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(untagged))]
11pub enum ComponentValue<'a> {
12 SimpleBlock(SimpleBlock<'a>),
13 Function(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> Peek<'a> for ComponentValue<'a> {
29 fn peek(_: &Parser<'a>, c: Cursor) -> bool {
30 let 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 c == kindset
49 }
50}
51
52impl<'a> Parse<'a> for ComponentValue<'a> {
54 fn parse(p: &mut Parser<'a>) -> ParserResult<Self> {
55 let c = p.peek_n(1);
56 Ok(if <T![' ']>::peek(p, c) {
57 Self::Whitespace(p.parse::<T![' ']>()?)
58 } else if <T![PairWiseStart]>::peek(p, c) {
59 let old_state = p.set_state(State::Nested);
60 let block = p.parse::<SimpleBlock>();
61 p.set_state(old_state);
62 Self::SimpleBlock(block?)
63 } else if <T![Function]>::peek(p, c) {
64 Self::Function(p.parse::<FunctionBlock>()?)
65 } else if <T![Number]>::peek(p, c) {
66 Self::Number(p.parse::<T![Number]>()?)
67 } else if <T![Dimension]>::peek(p, c) {
68 Self::Dimension(p.parse::<T![Dimension]>()?)
69 } else if <T![Ident]>::peek(p, c) {
70 Self::Ident(p.parse::<T![Ident]>()?)
71 } else if <T![AtKeyword]>::peek(p, c) {
72 Self::AtKeyword(p.parse::<T![AtKeyword]>()?)
73 } else if <T![Hash]>::peek(p, c) {
74 Self::Hash(p.parse::<T![Hash]>()?)
75 } else if <T![String]>::peek(p, c) {
76 Self::String(p.parse::<T![String]>()?)
77 } else if <T![Url]>::peek(p, c) {
78 Self::Url(p.parse::<T![Url]>()?)
79 } else if <T![Delim]>::peek(p, c) {
80 p.parse::<T![Delim]>().map(|delim| {
81 let mut rules = AssociatedWhitespaceRules::none();
83 if p.peek_n_with_skip(1, KindSet::COMMENTS) == Kind::Whitespace {
84 rules |= AssociatedWhitespaceRules::EnforceAfter;
85 } else {
86 rules |= AssociatedWhitespaceRules::BanAfter;
87 }
88 Self::Delim(delim.with_associated_whitespace(rules))
89 })?
90 } else if <T![:]>::peek(p, c) {
91 Self::Colon(p.parse::<T![:]>()?)
92 } else if <T![;]>::peek(p, c) {
93 Self::Semicolon(p.parse::<T![;]>()?)
94 } else if <T![,]>::peek(p, c) {
95 Self::Comma(p.parse::<T![,]>()?)
96 } else {
97 Err(Diagnostic::new(p.next(), Diagnostic::unexpected))?
98 })
99 }
100}
101
102impl<'a> ToCursors for ComponentValue<'a> {
103 fn to_cursors(&self, s: &mut impl CursorSink) {
104 match self {
105 Self::SimpleBlock(t) => ToCursors::to_cursors(t, s),
106 Self::Function(t) => ToCursors::to_cursors(t, s),
107 Self::Ident(t) => ToCursors::to_cursors(t, s),
108 Self::AtKeyword(t) => ToCursors::to_cursors(t, s),
109 Self::Hash(t) => ToCursors::to_cursors(t, s),
110 Self::String(t) => ToCursors::to_cursors(t, s),
111 Self::Url(t) => ToCursors::to_cursors(t, s),
112 Self::Delim(t) => ToCursors::to_cursors(t, s),
113 Self::Number(t) => ToCursors::to_cursors(t, s),
114 Self::Dimension(t) => ToCursors::to_cursors(t, s),
115 Self::Whitespace(t) => ToCursors::to_cursors(t, s),
116 Self::Colon(t) => ToCursors::to_cursors(t, s),
117 Self::Semicolon(t) => ToCursors::to_cursors(t, s),
118 Self::Comma(t) => ToCursors::to_cursors(t, s),
119 }
120 }
121}
122
123impl<'a> ToSpan for ComponentValue<'a> {
124 fn to_span(&self) -> Span {
125 match self {
126 Self::SimpleBlock(t) => t.to_span(),
127 Self::Function(t) => t.to_span(),
128 Self::Ident(t) => t.to_span(),
129 Self::AtKeyword(t) => t.to_span(),
130 Self::Hash(t) => t.to_span(),
131 Self::String(t) => t.to_span(),
132 Self::Url(t) => t.to_span(),
133 Self::Delim(t) => t.to_span(),
134 Self::Number(t) => t.to_span(),
135 Self::Dimension(t) => t.to_span(),
136 Self::Whitespace(t) => t.to_span(),
137 Self::Colon(t) => t.to_span(),
138 Self::Semicolon(t) => t.to_span(),
139 Self::Comma(t) => t.to_span(),
140 }
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use crate::{EmptyAtomSet, test_helpers::*};
148
149 #[test]
150 fn size_test() {
151 assert_eq!(std::mem::size_of::<ComponentValue>(), 64);
152 }
153
154 #[test]
155 fn test_writes() {
156 assert_parse!(EmptyAtomSet::ATOMS, ComponentValue, "foo");
157 assert_parse!(EmptyAtomSet::ATOMS, ComponentValue, " ");
158 assert_parse!(EmptyAtomSet::ATOMS, ComponentValue, "{block}");
159 }
160}