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 if let Time::Ms(dim) = time {
27 let seconds = time.as_seconds();
28 let sc = self.transformer.to_source_cursor((*dim).into());
29 let value = if seconds.fract() == 0.0 { format!("{}", seconds as i64) } else { format!("{seconds}") };
30 let seconds_len = value.len() - value.starts_with("0.") as usize - value.starts_with("-0.") as usize + 1;
31 let ms_len = if sc.may_compact() {
32 format!("{}", self.transformer.to_source_cursor((*dim).into()).compact()).len()
33 } else {
34 sc.token().len() as usize
35 };
36 if seconds_len < ms_len {
37 self.transformer.replace_parsed::<Time>(time.to_span(), &format!("{value}s"));
38 }
39 }
40 }
41}
42
43#[cfg(test)]
44mod tests {
45 use crate::test_helpers::{assert_no_transform, assert_transform};
46 use css_ast::{CssAtomSet, StyleSheet};
47
48 #[test]
49 fn converts_milliseconds_to_seconds() {
50 assert_transform!(
51 CssMinifierFeature::ReduceTimeUnits,
52 CssAtomSet,
53 StyleSheet,
54 "div { transition-duration: 500ms; }",
55 "div { transition-duration: 0.5s; }"
56 );
57 }
58
59 #[test]
60 fn keeps_shorter_millisecond_values() {
61 assert_no_transform!(
62 CssMinifierFeature::ReduceTimeUnits,
63 CssAtomSet,
64 StyleSheet,
65 "div { transition-duration: 50ms; }"
66 );
67 }
68
69 #[test]
70 fn keeps_second_values_when_not_shorter() {
71 assert_no_transform!(
72 CssMinifierFeature::ReduceTimeUnits,
73 CssAtomSet,
74 StyleSheet,
75 "div { transition-duration: 2s; }"
76 );
77 }
78
79 #[test]
80 fn converts_whole_seconds() {
81 assert_transform!(
82 CssMinifierFeature::ReduceTimeUnits,
83 CssAtomSet,
84 StyleSheet,
85 "div { animation-duration: 1000ms; }",
86 "div { animation-duration: 1s; }"
87 );
88 }
89
90 #[test]
91 fn converts_only_when_compact_is_larger() {
92 assert_no_transform!(
93 CssMinifierFeature::ReduceTimeUnits,
94 CssAtomSet,
95 StyleSheet,
96 "div { animation-duration: 00050ms; }"
97 );
98 }
99}