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