Repository: typst/typst
File: crates/typst-utils/src/format.rs (Rust)

Code before:
    let ms = format!(
        "{1:.0$} ms",
        if mcs != 0 { min(digits, mcs_digits) } else { 0 },
        ms as f32 + mcs as f32 / 1000.0
    );
    match (d, h, m, s) {
        (0, 0, 0, 0) => ms.to_string(),
        (d, h, m, s) => format_dhms(d, h, m, s),
    }
}

fn time_as_ms(duration: &Duration) -> String {
    time_starting_with_ms_with_precision(duration, 2)
}

pub fn elapsed_time(duration: &Duration) -> String {
    time_starting_with_seconds(duration)
}

pub fn eta_time(duration: &Duration) -> String {
    time_starting_with_seconds(duration)
}

pub fn compilation_time(duration: &Duration) -> String {
    time_as_ms(duration)
}

#[cfg(test)]
mod tests {
    use super::*;

    fn duration_milli_micro(milliseconds: u16, microseconds: u16) -> Duration {
        let microseconds = microseconds as u64;
        let milliseconds = 1000 * milliseconds as u64;
        Duration::from_micros(milliseconds + microseconds)
    }

    #[test]
    fn test_time_starting_with_seconds() {
        let f = |duration| time_starting_with_seconds(&duration);
        fn duration(days: u16, hours: u8, minutes: u8, seconds: u8) -> Duration {
            let seconds = seconds as u64;
            let minutes = 60 * minutes as u64;
            let hours = 60 * 60 * hours as u64;
            let days = 24 * 60 * 60 * days as u64;
            Duration::from_secs(days + hours + minutes + seconds)
        }
        assert_eq!(" 0 s", &f(duration(0, 0, 0, 0)));
        assert_eq!("59 s", &f(duration(0, 0, 0, 59)));
        assert_eq!(" 1 m 12 s", &f(duration(0, 0, 1, 12)));
        assert_eq!("59 m  0 s", &f(duration(0, 0, 59, 0)));

Review comment (suggestion):
I believe it should be fine to inline these functions into their respective usages. Makes more sense for the format choice to be specified where they're used instead of in a different module.

Code after:
    let (mins, sec) = (sec / 60, (sec % 60) as u8);
    let (hours, mins) = (mins / 60, (mins % 60) as u8);
    let (days, hours) = ((hours / 24) as u64, (hours % 24) as u8);
    (days, hours, mins, sec, millis, micros)
}

/// Format string using `days`, `hours`, `minutes`, `seconds`.
fn format_dhms(days: u64, hours: u8, minutes: u8, seconds: u8) -> String {
    match (days, hours, minutes, seconds) {
        (0, 0, 0, s) => format!("{s:2} s"),
        (0, 0, m, s) => format!("{m:2} m {s:2} s"),
        (0, h, m, s) => format!("{h:2} h {m:2} m {s:2} s"),
        (d, h, m, s) => format!("{d:3} d {h:2} h {m:2} m {s:2} s"),
    }
}

/// Format string starting with number of seconds and going bigger from there.
///
/// # Examples
///
/// ```
/// # use std::time::Duration;
/// # use typst_utils::format::time_starting_with_seconds;
/// let duration1 = time_starting_with_seconds(&Duration::from_secs(0));
/// assert_eq!(" 0 s", &duration1);
///
/// let duration2 = time_starting_with_seconds(&Duration::from_secs(
///     24 * 60 * 60 * 100 + // days
///     60 * 60 * 10 + // hours
///     60 * 10 + // minutes
///     10 // seconds
/// ));
/// assert_eq!("100 d 10 h 10 m 10 s", &duration2);
/// ```
pub fn time_starting_with_seconds(duration: &Duration) -> String {
    let (days, hours, minutes, seconds, _, _) = get_duration_parts(duration);
    format_dhms(days, hours, minutes, seconds)
}

/// Format string starting with number of milliseconds and going bigger
/// from there. `precision` is how many digits of microseconds
/// from floating point to the right will be preserved (with rounding).
/// Keep in mind that this function will always remove all trailing zeros
/// for microseconds.
///
/// Note: if duration is 1 second or longer, then output will be identical
/// to [time_starting_with_seconds], which also means that precision,
/// number of milliseconds and microseconds will not be used.
///
/// # Examples
///