csskit_transform/
reduce_time_units.rs1use crate::prelude::*;
2use css_ast::{Time, Visitable};
3
4pub struct ReduceTimeUnits<'a, 'ctx, N: Visitable + NodeWithMetadata<CssMetadata>> {
5 pub transformer: &'ctx Transformer<'a, CssMetadata, N, CssMinifierFeature>,
6}
7
8impl<'a, 'ctx, N> Transform<'a, 'ctx, CssMetadata, N, CssMinifierFeature> for ReduceTimeUnits<'a, 'ctx, N>
9where
10 N: Visitable + NodeWithMetadata<CssMetadata>,
11{
12 fn may_change(features: CssMinifierFeature, _node: &N) -> bool {
13 features.contains(CssMinifierFeature::ReduceTimeUnits)
14 }
15
16 fn new(transformer: &'ctx Transformer<'a, CssMetadata, N, CssMinifierFeature>) -> Self {
17 Self { transformer }
18 }
19}
20
21impl<'a, 'ctx, N> Visit for ReduceTimeUnits<'a, 'ctx, N>
22where
23 N: Visitable + NodeWithMetadata<CssMetadata>,
24{
25 fn visit_time(&mut self, time: &Time) {
26 let original_len = time.to_span().len() as usize;
27 let seconds = time.as_seconds();
28
29 if seconds == 0.0 && original_len > 1 {
30 self.transformer.replace_parsed::<Time>(time.to_span(), "0");
31 return;
32 }
33
34 if let Time::Ms(dim) = time {
35 let sc = self.transformer.to_source_cursor((*dim).into());
36 let value = if seconds.fract() == 0.0 { format!("{}", seconds as i64) } else { format!("{seconds}") };
37 let seconds_len = value.len() - value.starts_with("0.") as usize - value.starts_with("-0.") as usize + 1;
38 let ms_len = if sc.may_compact() {
39 format!("{}", self.transformer.to_source_cursor((*dim).into()).compact()).len()
40 } else {
41 sc.token().len() as usize
42 };
43 if seconds_len < ms_len {
44 self.transformer.replace_parsed::<Time>(time.to_span(), &format!("{value}s"));
45 }
46 }
47 }
48}
49
50#[cfg(test)]
51mod tests {
52 use crate::test_helpers::{assert_no_transform, assert_transform};
53 use css_ast::{CssAtomSet, StyleSheet};
54
55 #[test]
56 fn converts_milliseconds_to_seconds() {
57 assert_transform!(
58 CssMinifierFeature::ReduceTimeUnits,
59 CssAtomSet,
60 StyleSheet,
61 "div { transition-duration: 500ms; }",
62 "div { transition-duration: 0.5s; }"
63 );
64 }
65
66 #[test]
67 fn keeps_shorter_millisecond_values() {
68 assert_no_transform!(
69 CssMinifierFeature::ReduceTimeUnits,
70 CssAtomSet,
71 StyleSheet,
72 "div { transition-duration: 50ms; }"
73 );
74 }
75
76 #[test]
77 fn normalizes_zero_units() {
78 assert_transform!(
79 CssMinifierFeature::ReduceTimeUnits,
80 CssAtomSet,
81 StyleSheet,
82 "div { transition-delay: 0ms; animation-duration: 0s; }",
83 "div { transition-delay: 0; animation-duration: 0; }"
84 );
85 }
86
87 #[test]
88 fn keeps_second_values_when_not_shorter() {
89 assert_no_transform!(
90 CssMinifierFeature::ReduceTimeUnits,
91 CssAtomSet,
92 StyleSheet,
93 "div { transition-duration: 2s; }"
94 );
95 }
96
97 #[test]
98 fn converts_whole_seconds() {
99 assert_transform!(
100 CssMinifierFeature::ReduceTimeUnits,
101 CssAtomSet,
102 StyleSheet,
103 "div { animation-duration: 1000ms; }",
104 "div { animation-duration: 1s; }"
105 );
106 }
107
108 #[test]
109 fn converts_only_when_compact_is_larger() {
110 assert_no_transform!(
111 CssMinifierFeature::ReduceTimeUnits,
112 CssAtomSet,
113 StyleSheet,
114 "div { animation-duration: 00050ms; }"
115 );
116 }
117}