1use super::{HumanDuration, HumanDurationData};
2use crate::utils::{self, SPACE};
3use std::{fmt, time::Duration};
4
5const SPEC: &[(f64, f64, &str, usize)] = &[
6 (1e3, 1e3, "ns", 1),
7 (1e3, 1e3, "µs", 1), (1e3, 1e3, "ms", 1),
9 (60., 1., "s", 2),
10 ];
13
14impl fmt::Display for HumanDurationData {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 let HumanDurationData { mut val } = self;
17 val *= 1e9;
18 for &(size, next, scale, dec) in SPEC {
19 match utils::rounded(val, dec) {
20 r if r.abs() >= size => val /= next,
21 r if r.fract() == 0. => return write!(f, "{:.0}{}{}", r, SPACE, scale),
22 r if (r * 10.).fract() == 0. => return write!(f, "{:.1}{}{}", r, SPACE, scale),
23 r => return write!(f, "{:.2}{}{}", r, SPACE, scale),
24 }
25 }
26
27 val = utils::rounded(val, 1);
28 let (m, s) = (val / 60., val % 60.);
29 match m < 60. {
30 true => match s {
31 _ if s.fract() == 0. => write!(f, "{}:{:02}", m.trunc(), s),
32 _ => write!(f, "{}:{:04}", m.trunc(), utils::rounded(s, 1)),
33 },
34 false => write!(
35 f,
36 "{}:{:02}:{:02}",
37 (m / 60.).trunc(),
38 (m % 60.).trunc(),
39 s.trunc()
40 ),
41 }
42 }
43}
44
45impl fmt::Debug for HumanDurationData {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 f.debug_struct("HumanDuration")
48 .field("val", &self.val)
49 .finish()?;
50 write!(f, " -> ")?;
51 fmt::Display::fmt(self, f)
52 }
53}
54
55impl PartialEq<HumanDurationData> for &str {
56 fn eq(&self, other: &HumanDurationData) -> bool {
57 utils::display_compare(self, other)
58 }
59}
60
61impl PartialEq<&str> for HumanDurationData {
62 fn eq(&self, other: &&str) -> bool {
63 other == self
64 }
65}
66
67impl From<Duration> for HumanDurationData {
68 fn from(d: Duration) -> Self {
69 d.as_secs_f64().human_duration()
70 }
71}
72
73impl HumanDuration for Duration {
74 fn human_duration(self) -> HumanDurationData {
75 self.into()
76 }
77}
78
79#[cfg(all(test, not(any(feature = "1024", feature = "iec", feature = "space"))))]
80mod tests {
81 use crate::HumanDuration;
82
83 #[test]
84 fn operation() {
85 assert_eq!("1s", 1.human_duration());
86 assert_eq!("-1s", (-1).human_duration());
87 assert_eq!("1.2ns", 0.00000000123.human_duration());
88 assert_eq!("1.8ns", 0.0000000018.human_duration());
89 assert_eq!("1µs", 0.000001.human_duration());
90 assert_eq!("-1µs", (-0.000001).human_duration());
91 assert_eq!("1µs", 0.000000999996.human_duration());
92 assert_eq!("10µs", 0.00001.human_duration());
93 assert_eq!("15.6µs", 0.0000156.human_duration());
94 assert_eq!("10ms", 0.01.human_duration());
95 assert_eq!("14.1ms", 0.0141233333333.human_duration());
96 assert_eq!("1ms", 0.000999999.human_duration());
97 assert_eq!("20ms", 0.0199999.human_duration());
98 assert_eq!("110ms", 0.1099999.human_duration());
99 assert_eq!("160ms", 0.1599999.human_duration());
100 assert_eq!("801.5ms", 0.8015.human_duration());
101 assert_eq!("3.43s", 3.434999.human_duration());
102 assert_eq!("3.44s", 3.435999.human_duration());
103 assert_eq!("59s", 59.0.human_duration());
104 assert_eq!("59.9s", 59.9.human_duration());
105 assert_eq!("59.99s", 59.99.human_duration());
106 assert_eq!("1:00", 59.995.human_duration());
107 assert_eq!("1:08.1", 68.09.human_duration());
108 assert_eq!("2:05.8", 125.825.human_duration());
109 assert_eq!("19:20.4", 1160.36.human_duration());
110 assert_eq!("1:04:48", 3888.395.human_duration());
111 assert_eq!("2:46:40", 10000u16.human_duration());
112 assert_eq!("27:46:40", 100000i64.human_duration());
113 assert_eq!("277:46:40", 1000000isize.human_duration());
114 }
115
116 #[test]
117 fn flexibility() {
118 use crate::HumanDuration;
119 use std::time::Duration;
120 macro_rules! d {
121 {$f:literal} => {
122 Duration::from_secs_f64($f)
123 };
124 {$s:literal, $n:literal} => {
125 Duration::new($s, $n)
126 };
127 }
128
129 assert_eq!("1s", d!(1.).human_duration());
130 assert_eq!("1.5s", d!(1.5).human_duration());
131 assert_eq!("1ns", d!(0.00000000123).human_duration());
132 assert_eq!("2ns", d!(0.00000000185).human_duration());
133 assert_eq!("1ns", d!(0, 1).human_duration());
134 assert_eq!("1µs", d!(0.000000999999999).human_duration());
135 assert_eq!("1µs", d!(0, 1000).human_duration());
136 assert_eq!("10µs", d!(0, 10000).human_duration());
137 assert_eq!("15.6µs", d!(0, 15600).human_duration());
138 assert_eq!("10ms", d!(0.01).human_duration());
139 assert_eq!("14.1ms", d!(0.0141233333333).human_duration());
140 assert_eq!("110ms", d!(0, 110000000).human_duration());
141 assert_eq!("801.5ms", d!(0.8015).human_duration());
142 assert_eq!("3.43s", d!(3.434999).human_duration());
143 assert_eq!("59s", d!(59.0).human_duration());
144 assert_eq!("59.9s", d!(59.9).human_duration());
145 assert_eq!("59.99s", d!(59.99).human_duration());
146 assert_eq!("1:00", d!(60, 0).human_duration());
147 assert_eq!("1:08.1", d!(68.09).human_duration());
148 assert_eq!("19:20.4", d!(1160, 350000000).human_duration());
149 assert_eq!("1:04:48", d!(3888.395).human_duration());
150 assert_eq!("2:46:40", d!(10000.).human_duration());
151 assert_eq!("27:46:40", d!(100000.).human_duration());
152 assert_eq!("277:46:40", d!(1000000, 1).human_duration());
153 }
154
155 #[test]
156 #[allow(clippy::needless_borrow)]
157 fn ownership() {
158 let mut a = 0.01;
159 assert_eq!("10ms", a.human_duration());
160 assert_eq!("10ms", (&a).human_duration());
161 assert_eq!("10ms", (&mut a).human_duration());
162 }
163
164 #[test]
165 fn symmetric() {
166 assert_eq!(1.human_duration(), "1s");
167 }
168}
169
170#[test]
171#[cfg(feature = "serde")]
172fn serialize() -> Result<(), serde_json::Error> {
173 use HumanDuration;
174 let h = 123456.human_duration();
175 let ser = serde_json::to_string(&h)?;
176 assert_eq!(r#"{"val":123456.0}"#, &ser);
177 let h2 = serde_json::from_str::<HumanDurationData>(&ser)?;
178 assert_eq!(h, h2);
179 Ok(())
180}