1use cssparser::{ParseError, ParseErrorKind, Parser, ParserInput, Token};
4use serde::{
5 Deserialize, Deserializer,
6 de::{self, MapAccess, SeqAccess, Visitor},
7};
8
9use crate::style::{KeyframeRule, KeyframesRule, StyleDeclarationBlock};
10
11struct StageMap(Vec<(String, StyleDeclarationBlock)>);
12
13impl<'de> Deserialize<'de> for StageMap {
14 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
15 where
16 D: Deserializer<'de>,
17 {
18 struct StageMapVisitor;
19
20 impl<'de> Visitor<'de> for StageMapVisitor {
21 type Value = StageMap;
22
23 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 formatter.write_str("a map from keyframe selector to declaration block")
25 }
26
27 fn visit_map<A>(self, mut access: A) -> Result<Self::Value, A::Error>
28 where
29 A: MapAccess<'de>,
30 {
31 let mut stages = Vec::with_capacity(access.size_hint().unwrap_or(0));
32 while let Some((selector, declarations)) =
33 access.next_entry::<String, StyleDeclarationBlock>()?
34 {
35 stages.push((selector, declarations));
36 }
37 stages.sort_unstable_by(|(left, _), (right, _)| left.cmp(right));
38 Ok(StageMap(stages))
39 }
40 }
41
42 deserializer.deserialize_map(StageMapVisitor)
43 }
44}
45
46struct KeyframesVec(Vec<KeyframesRule>);
47
48impl<'de> Deserialize<'de> for KeyframesVec {
49 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
50 where
51 D: Deserializer<'de>,
52 {
53 struct KeyframesVecVisitor;
54
55 impl<'de> Visitor<'de> for KeyframesVecVisitor {
56 type Value = KeyframesVec;
57
58 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 formatter.write_str("keyframes rules array or shorthand keyframes object")
60 }
61
62 fn visit_seq<A>(self, mut access: A) -> Result<Self::Value, A::Error>
63 where
64 A: SeqAccess<'de>,
65 {
66 let mut rules = Vec::with_capacity(access.size_hint().unwrap_or(0));
67 while let Some(rule) = access.next_element::<KeyframesRule>()? {
68 rules.push(rule);
69 }
70 Ok(KeyframesVec(rules))
71 }
72
73 fn visit_map<A>(self, mut access: A) -> Result<Self::Value, A::Error>
74 where
75 A: MapAccess<'de>,
76 {
77 let mut shorthand = Vec::with_capacity(access.size_hint().unwrap_or(0));
78 while let Some((name, stages)) = access.next_entry::<String, StageMap>()? {
79 shorthand.push((name, stages));
80 }
81 shorthand.sort_unstable_by(|(left, _), (right, _)| left.cmp(right));
82
83 let rules = shorthand
84 .into_iter()
85 .map(|(name, StageMap(stages))| {
86 let keyframes = stages
87 .into_iter()
88 .map(|(selector, declarations)| {
89 Ok(KeyframeRule {
90 offsets: parse_keyframe_offsets(&selector).map_err(de::Error::custom)?,
91 declarations,
92 })
93 })
94 .collect::<Result<Vec<_>, _>>()?;
95
96 Ok(KeyframesRule {
97 name,
98 keyframes,
99 media_queries: Vec::new(),
100 })
101 })
102 .collect::<Result<Vec<_>, _>>()?;
103
104 Ok(KeyframesVec(rules))
105 }
106 }
107
108 deserializer.deserialize_any(KeyframesVecVisitor)
109 }
110}
111
112pub fn deserialize_keyframes<'de, D>(deserializer: D) -> Result<Vec<KeyframesRule>, D::Error>
114where
115 D: Deserializer<'de>,
116{
117 Ok(KeyframesVec::deserialize(deserializer)?.0)
118}
119
120pub fn deserialize_optional_keyframes<'de, D>(
122 deserializer: D,
123) -> Result<Option<Vec<KeyframesRule>>, D::Error>
124where
125 D: Deserializer<'de>,
126{
127 Ok(Option::<KeyframesVec>::deserialize(deserializer)?.map(|keyframes| keyframes.0))
128}
129
130fn parse_keyframe_offsets(selector: &str) -> Result<Vec<f32>, String> {
131 if selector
132 .trim_matches(|c: char| c.is_ascii_whitespace() || c == ',')
133 .is_empty()
134 {
135 return Err(
136 "empty keyframe selector; expected at least one of `from`, `to`, or percentage values"
137 .to_owned(),
138 );
139 }
140
141 let mut input = ParserInput::new(selector);
142 let mut parser = Parser::new(&mut input);
143 parse_keyframe_prelude::<KeyframePreludeParseError<'_>>(&mut parser).map_err(|error| match error
144 .kind
145 {
146 ParseErrorKind::Custom(KeyframePreludeParseError::InvalidPercentage(part)) => {
147 format!("invalid keyframe percentage `{part}`; expected a value in 0%..=100%")
148 }
149 ParseErrorKind::Custom(KeyframePreludeParseError::InvalidSelector(part)) => {
150 unsupported_keyframe_selector(part)
151 }
152 ParseErrorKind::Basic(_) => unsupported_keyframe_selector(selector.trim()),
153 })
154}
155
156pub(crate) fn parse_keyframe_prelude<'i, E>(
157 input: &mut Parser<'i, '_>,
158) -> Result<Vec<f32>, ParseError<'i, E>>
159where
160 KeyframePreludeParseError<'i>: Into<E>,
161{
162 input.parse_comma_separated(parse_keyframe_offset)
163}
164
165#[derive(Clone, Copy)]
166pub(crate) enum KeyframePreludeParseError<'i> {
167 InvalidSelector(&'i str),
168 InvalidPercentage(&'i str),
169}
170
171fn parse_keyframe_offset<'i, E>(input: &mut Parser<'i, '_>) -> Result<f32, ParseError<'i, E>>
172where
173 KeyframePreludeParseError<'i>: Into<E>,
174{
175 if input
176 .try_parse(|parser| parser.expect_ident_matching("from"))
177 .is_ok()
178 {
179 return Ok(0.0);
180 }
181
182 if input
183 .try_parse(|parser| parser.expect_ident_matching("to"))
184 .is_ok()
185 {
186 return Ok(1.0);
187 }
188
189 let start_position = input.position();
190 let offset = match input.next() {
191 Ok(Token::Percentage { unit_value, .. }) => *unit_value,
192 Ok(_) | Err(_) => {
193 let part = input.slice_from(start_position).trim();
194 return Err(input.new_custom_error(KeyframePreludeParseError::InvalidSelector(part)));
195 }
196 };
197
198 if !(0.0..=1.0).contains(&offset) {
199 let part = input.slice_from(start_position).trim();
200 return Err(input.new_custom_error(KeyframePreludeParseError::InvalidPercentage(part)));
201 }
202
203 Ok(offset)
204}
205
206fn unsupported_keyframe_selector(selector: &str) -> String {
207 format!(
208 "unsupported keyframe selector `{selector}`; use `from`, `to`, or percentage values like `50%`"
209 )
210}
211
212#[cfg(test)]
213mod tests {
214 use std::assert_matches;
215
216 use serde::Deserialize;
217 use serde_json::from_value;
218
219 use super::{deserialize_keyframes, deserialize_optional_keyframes};
220 use crate::style::KeyframesRule;
221
222 #[derive(Debug, Deserialize)]
223 struct KeyframesDocument {
224 #[serde(deserialize_with = "deserialize_keyframes")]
225 keyframes: Vec<KeyframesRule>,
226 }
227
228 #[derive(Debug, Deserialize)]
229 struct OptionalKeyframesDocument {
230 #[serde(default, deserialize_with = "deserialize_optional_keyframes")]
231 keyframes: Option<Vec<KeyframesRule>>,
232 }
233
234 #[test]
235 fn rejects_empty_keyframe_selector() {
236 let result = from_value::<KeyframesDocument>(serde_json::json!({
237 "keyframes": {
238 "fade": {
239 " , ": {
240 "opacity": 0
241 }
242 }
243 }
244 }));
245
246 assert!(
247 result.is_err(),
248 "expected empty selector to fail: {result:?}"
249 );
250 assert_matches!(
251 result.as_ref(),
252 Err(error) if error.to_string().contains("empty keyframe selector")
253 );
254 }
255
256 #[test]
257 fn parses_shorthand_keyframes() {
258 let result = from_value::<KeyframesDocument>(serde_json::json!({
259 "keyframes": {
260 "fade": {
261 "from, 50%, to": {
262 "opacity": 1
263 }
264 }
265 }
266 }));
267
268 assert!(
269 result.is_ok(),
270 "expected valid shorthand keyframes: {result:?}"
271 );
272 let keyframes = result.as_ref().ok();
273
274 assert_eq!(keyframes.map(|value| value.keyframes.len()), Some(1));
275 assert_eq!(
276 keyframes.map(|value| value.keyframes[0].keyframes[0].offsets.clone()),
277 Some(vec![0.0, 0.5, 1.0])
278 );
279 }
280
281 #[test]
282 fn keeps_missing_optional_keyframes_as_none() {
283 let result = from_value::<OptionalKeyframesDocument>(serde_json::json!({}));
284
285 assert!(
286 result.is_ok(),
287 "expected missing keyframes to deserialize: {result:?}"
288 );
289 assert_eq!(result.ok().and_then(|document| document.keyframes), None);
290 }
291}