use crate::prelude::*;
use css_ast::{Time, Visitable};
pub struct ReduceTimeUnits<'a, 'ctx, N: Visitable + NodeWithMetadata<CssMetadata>> {
pub transformer: &'ctx Transformer<'a, CssMetadata, N, CssMinifierFeature>,
}
impl<'a, 'ctx, N> Transform<'a, 'ctx, CssMetadata, N, CssMinifierFeature> for ReduceTimeUnits<'a, 'ctx, N>
where
N: Visitable + NodeWithMetadata<CssMetadata>,
{
fn may_change(features: CssMinifierFeature, _node: &N) -> bool {
features.contains(CssMinifierFeature::ReduceTimeUnits)
}
fn new(transformer: &'ctx Transformer<'a, CssMetadata, N, CssMinifierFeature>) -> Self {
Self { transformer }
}
}
impl<'a, 'ctx, N> Visit for ReduceTimeUnits<'a, 'ctx, N>
where
N: Visitable + NodeWithMetadata<CssMetadata>,
{
fn visit_time(&mut self, time: &Time) {
let original_len = time.to_span().len() as usize;
let seconds = time.as_seconds();
if seconds == 0.0 && original_len > 1 {
self.transformer.replace_parsed::<Time>(time.to_span(), "0");
return;
}
if let Time::Ms(dim) = time {
let sc = self.transformer.to_source_cursor((*dim).into());
let value = if seconds.fract() == 0.0 { format!("{}", seconds as i64) } else { format!("{seconds}") };
let seconds_len = value.len() - value.starts_with("0.") as usize - value.starts_with("-0.") as usize + 1;
let ms_len = if sc.may_compact() {
format!("{}", self.transformer.to_source_cursor((*dim).into()).compact()).len()
} else {
sc.token().len() as usize
};
if seconds_len < ms_len {
self.transformer.replace_parsed::<Time>(time.to_span(), &format!("{value}s"));
}
}
}
}
#[cfg(test)]
mod tests {
use crate::test_helpers::{assert_no_transform, assert_transform};
use css_ast::{CssAtomSet, StyleSheet};
#[test]
fn converts_milliseconds_to_seconds() {
assert_transform!(
CssMinifierFeature::ReduceTimeUnits,
CssAtomSet,
StyleSheet,
"div { transition-duration: 500ms; }",
"div { transition-duration: 0.5s; }"
);
}
#[test]
fn keeps_shorter_millisecond_values() {
assert_no_transform!(
CssMinifierFeature::ReduceTimeUnits,
CssAtomSet,
StyleSheet,
"div { transition-duration: 50ms; }"
);
}
#[test]
fn normalizes_zero_units() {
assert_transform!(
CssMinifierFeature::ReduceTimeUnits,
CssAtomSet,
StyleSheet,
"div { transition-delay: 0ms; animation-duration: 0s; }",
"div { transition-delay: 0; animation-duration: 0; }"
);
}
#[test]
fn keeps_second_values_when_not_shorter() {
assert_no_transform!(
CssMinifierFeature::ReduceTimeUnits,
CssAtomSet,
StyleSheet,
"div { transition-duration: 2s; }"
);
}
#[test]
fn converts_whole_seconds() {
assert_transform!(
CssMinifierFeature::ReduceTimeUnits,
CssAtomSet,
StyleSheet,
"div { animation-duration: 1000ms; }",
"div { animation-duration: 1s; }"
);
}
#[test]
fn converts_only_when_compact_is_larger() {
assert_no_transform!(
CssMinifierFeature::ReduceTimeUnits,
CssAtomSet,
StyleSheet,
"div { animation-duration: 00050ms; }"
);
}
}