css_parse/syntax/
component_values.rs1use super::prelude::*;
2use crate::AssociatedWhitespaceRules;
3
4use super::ComponentValue;
5
6#[node]
8#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
10pub struct ComponentValues<'a> {
11 pub values: Vec<'a, ComponentValue<'a>>,
12}
13
14impl<'a> Peek<'a> for ComponentValues<'a> {
15 const PEEK_KINDSET: KindSet = ComponentValue::PEEK_KINDSET;
16}
17
18impl<'a> Parse<'a> for ComponentValues<'a> {
19 fn parse<Iter>(p: &mut Parser<'a, Iter>) -> Result<Self>
21 where
22 Iter: Iterator<Item = Cursor> + Clone,
23 {
24 let mut values = Vec::new_in(p.alloc());
25 let mut last_was_whitespace = false;
26 let mut trailing_whitespace = None;
27
28 loop {
29 if p.at_end() {
30 break;
31 }
32 if p.next_is_stop() {
33 break;
34 }
35 if let Some(mut value) = p.parse_if_peek::<ComponentValue>()? {
36 if let ComponentValue::Delim(d) = value
37 && last_was_whitespace
38 && d.associated_whitespace().contains(AssociatedWhitespaceRules::EnforceAfter)
39 {
40 let rules = d.associated_whitespace() | AssociatedWhitespaceRules::EnforceBefore;
41 value = ComponentValue::Delim(d.with_associated_whitespace(rules))
42 }
43 last_was_whitespace = match value {
47 ComponentValue::Whitespace(ws) => {
48 if values.is_empty() {
49 value = ComponentValue::Whitespace(ws.with_significant_whitespace(false));
50 } else if trailing_whitespace.is_none() {
51 trailing_whitespace = Some(values.len());
52 }
53 true
54 }
55 _ => {
56 trailing_whitespace = None;
57 false
58 }
59 };
60 values.push(value);
61 } else {
62 break;
63 }
64 }
65
66 if let Some(index) = trailing_whitespace {
67 for value in &mut values[index..] {
68 if let ComponentValue::Whitespace(ws) = value {
69 *value = ComponentValue::Whitespace(ws.with_significant_whitespace(false));
70 }
71 }
72 }
73 Ok(Self { values })
74 }
75}
76
77impl<'a, M: NodeMetadata> NodeWithMetadata<M> for ComponentValues<'a> {
78 fn metadata(&self) -> M {
79 M::default()
80 }
81}
82
83impl<'a> DeclarationValue<'a, ()> for ComponentValues<'a> {
84 fn parse_custom_declaration_value<Iter>(p: &mut Parser<'a, Iter>, _name: Cursor) -> Result<Self>
85 where
86 Iter: Iterator<Item = crate::Cursor> + Clone,
87 {
88 Self::parse(p)
89 }
90
91 fn is_computed_declaration_value<Iter>(p: &Parser<'a, Iter>, c: Cursor) -> bool
92 where
93 Iter: Iterator<Item = crate::Cursor> + Clone,
94 {
95 <Self as Peek>::peek(p, c)
96 }
97
98 fn parse_computed_declaration_value<Iter>(p: &mut Parser<'a, Iter>, _name: Cursor) -> Result<Self>
99 where
100 Iter: Iterator<Item = crate::Cursor> + Clone,
101 {
102 Self::parse(p)
103 }
104
105 fn parse_unknown_declaration_value<Iter>(p: &mut Parser<'a, Iter>, _name: Cursor) -> Result<Self>
106 where
107 Iter: Iterator<Item = crate::Cursor> + Clone,
108 {
109 Self::parse(p)
110 }
111}
112
113impl<'a> ToCursors for ComponentValues<'a> {
114 fn to_cursors(&self, s: &mut impl CursorSink) {
115 ToCursors::to_cursors(&self.values, s)
116 }
117}
118
119impl<'a> ToSpan for ComponentValues<'a> {
120 fn to_span(&self) -> Span {
121 self.values.to_span()
122 }
123}
124
125impl<'a> SemanticEq for ComponentValues<'a> {
127 fn semantic_eq(&self, other: &Self) -> bool {
128 self.values.semantic_eq(&other.values)
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use crate::{EmptyAtomSet, test_helpers::*};
136
137 #[test]
138 fn test_writes() {
139 assert_parse!(EmptyAtomSet::ATOMS, ComponentValues, "body{color:black}");
140 assert_parse!(EmptyAtomSet::ATOMS, ComponentValues, "body");
141 }
142
143 #[test]
144 fn test_writes_with_trivia() {
145 assert_parse!(EmptyAtomSet::ATOMS, ComponentValues, "/*comment*/foo");
146 assert_parse!(EmptyAtomSet::ATOMS, ComponentValues, " /*comment*/ foo");
147 assert_parse!(EmptyAtomSet::ATOMS, ComponentValues, "/*a*/foo/*b*/bar");
148 assert_parse!(EmptyAtomSet::ATOMS, ComponentValues, "foo/*comment*/bar");
149 assert_parse!(EmptyAtomSet::ATOMS, ComponentValues, " \t foo");
150 assert_parse!(EmptyAtomSet::ATOMS, ComponentValues, " /*start*/ foo /*mid*/ bar");
151 assert_parse!(EmptyAtomSet::ATOMS, ComponentValues, "/*comment*/foo");
152 }
153}