mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
95 lines
2.8 KiB
Rust
95 lines
2.8 KiB
Rust
use std::fmt::{self, Display, Formatter, Write};
|
|
use std::time::Duration;
|
|
|
|
use super::round_with_precision;
|
|
|
|
/// Formats a duration with a precision suitable for human display.
|
|
pub fn format_duration(duration: Duration) -> impl Display {
|
|
DurationDisplay(duration)
|
|
}
|
|
|
|
/// Displays a `Duration`.
|
|
struct DurationDisplay(Duration);
|
|
|
|
impl Display for DurationDisplay {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
let mut space = false;
|
|
macro_rules! piece {
|
|
($($tts:tt)*) => {
|
|
if std::mem::replace(&mut space, true) {
|
|
f.write_char(' ')?;
|
|
}
|
|
write!(f, $($tts)*)?;
|
|
};
|
|
}
|
|
|
|
let secs = self.0.as_secs();
|
|
let (mins, secs) = (secs / 60, (secs % 60));
|
|
let (hours, mins) = (mins / 60, (mins % 60));
|
|
let (days, hours) = ((hours / 24), (hours % 24));
|
|
|
|
if days > 0 {
|
|
piece!("{days} d");
|
|
}
|
|
|
|
if hours > 0 {
|
|
piece!("{hours} h");
|
|
}
|
|
|
|
if mins > 0 {
|
|
piece!("{mins} min");
|
|
}
|
|
|
|
// No need to display anything more than minutes at this point.
|
|
if days > 0 || hours > 0 {
|
|
return Ok(());
|
|
}
|
|
|
|
let order = |exp| 1000u64.pow(exp);
|
|
let nanos = secs * order(3) + self.0.subsec_nanos() as u64;
|
|
let fract = |exp| round_with_precision(nanos as f64 / order(exp) as f64, 2);
|
|
|
|
if nanos == 0 || self.0 > Duration::from_secs(1) {
|
|
// For durations > 5 min, we drop the fractional part.
|
|
if self.0 > Duration::from_secs(300) {
|
|
piece!("{secs} s");
|
|
} else {
|
|
piece!("{} s", fract(3));
|
|
}
|
|
} else if self.0 > Duration::from_millis(1) {
|
|
piece!("{} ms", fract(2));
|
|
} else if self.0 > Duration::from_micros(1) {
|
|
piece!("{} µs", fract(1));
|
|
} else {
|
|
piece!("{} ns", fract(0));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[track_caller]
|
|
fn test(duration: Duration, expected: &str) {
|
|
assert_eq!(format_duration(duration).to_string(), expected);
|
|
}
|
|
|
|
#[test]
|
|
fn test_format_duration() {
|
|
test(Duration::from_secs(1000000), "11 d 13 h 46 min");
|
|
test(Duration::from_secs(3600 * 24), "1 d");
|
|
test(Duration::from_secs(3600), "1 h");
|
|
test(Duration::from_secs(3600 + 240), "1 h 4 min");
|
|
test(Duration::from_secs_f64(364.77), "6 min 4 s");
|
|
test(Duration::from_secs_f64(264.776), "4 min 24.78 s");
|
|
test(Duration::from_secs(3), "3 s");
|
|
test(Duration::from_secs_f64(2.8492), "2.85 s");
|
|
test(Duration::from_micros(734), "734 µs");
|
|
test(Duration::from_micros(294816), "294.82 ms");
|
|
test(Duration::from_nanos(1), "1 ns");
|
|
}
|
|
}
|