Skip to main content

dsi_progress_logger/
utils.rs

1/*
2 * SPDX-FileCopyrightText: 2023 Inria
3 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
4 *
5 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
6 */
7
8#[derive(Debug, Copy, Clone)]
9
10pub enum TimeUnit {
11    NanoSeconds,
12    MicroSeconds,
13    MilliSeconds,
14    Seconds,
15    Minutes,
16    Hours,
17    Days,
18}
19
20impl TimeUnit {
21    pub const VALUES: [TimeUnit; 7] = [
22        TimeUnit::NanoSeconds,
23        TimeUnit::MicroSeconds,
24        TimeUnit::MilliSeconds,
25        TimeUnit::Seconds,
26        TimeUnit::Minutes,
27        TimeUnit::Hours,
28        TimeUnit::Days,
29    ];
30
31    pub const fn label(&self) -> &'static str {
32        match self {
33            TimeUnit::NanoSeconds => "ns",
34            TimeUnit::MicroSeconds => "μs",
35            TimeUnit::MilliSeconds => "ms",
36            TimeUnit::Seconds => "s",
37            TimeUnit::Minutes => "m",
38            TimeUnit::Hours => "h",
39            TimeUnit::Days => "d",
40        }
41    }
42
43    pub const fn as_seconds(&self) -> f64 {
44        match self {
45            TimeUnit::NanoSeconds => 1.0e-9,
46            TimeUnit::MicroSeconds => 1.0e-6,
47            TimeUnit::MilliSeconds => 1.0e-3,
48            TimeUnit::Seconds => 1.0,
49            TimeUnit::Minutes => 60.0,
50            TimeUnit::Hours => 3600.0,
51            TimeUnit::Days => 86400.0,
52        }
53    }
54
55    pub const fn nice_time_unit(seconds: f64) -> Self {
56        let mut i = TimeUnit::VALUES.len();
57        while i > 0 {
58            i -= 1;
59            if seconds >= TimeUnit::VALUES[i].as_seconds() {
60                return TimeUnit::VALUES[i];
61            }
62        }
63        TimeUnit::NanoSeconds
64    }
65
66    pub const fn nice_speed_unit(seconds: f64) -> Self {
67        let mut i = 3;
68        while i < TimeUnit::VALUES.len() {
69            if seconds <= TimeUnit::VALUES[i].as_seconds() {
70                return TimeUnit::VALUES[i];
71            }
72            i += 1;
73        }
74        TimeUnit::Days
75    }
76
77    pub fn pretty_print(milliseconds: u128) -> String {
78        let mut result = String::new();
79
80        if milliseconds < 1000 {
81            return format!("{}ms", milliseconds);
82        }
83
84        let mut seconds = milliseconds / 1000;
85
86        for unit in [TimeUnit::Days, TimeUnit::Hours, TimeUnit::Minutes] {
87            let to_seconds = unit.as_seconds() as u128;
88            if seconds >= to_seconds {
89                result.push_str(&format!("{}{} ", seconds / to_seconds, unit.label(),));
90                seconds %= to_seconds;
91            }
92        }
93
94        result.push_str(&format!("{}s", seconds));
95
96        result
97    }
98}
99
100pub const fn scale(mut val: f64) -> (f64, &'static str) {
101    const UNITS: &[&str] = &["", "k", "M", "G", "T", "P", "E", "Z", "Y"];
102    let mut i = 0;
103    while i < UNITS.len() {
104        if val < 1000.0 {
105            return (val, UNITS[i]);
106        }
107        val /= 1000.0;
108        i += 1;
109    }
110
111    (val, "Y")
112}
113
114pub fn humanize(val: f64) -> String {
115    let (val, unit) = scale(val);
116    format!("{:.2}{}", val, unit)
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122    #[test]
123    fn test_scale() {
124        assert_eq!(scale(1000.0), (1.0, "k"));
125        assert_eq!(scale(300_000.0), (300.0, "k"));
126        assert_eq!(scale(1_000_000_000.0), (1.0, "G"));
127    }
128    #[test]
129
130    fn test_humanize() {
131        assert_eq!(humanize(1000.0), "1.00k");
132        assert_eq!(humanize(12_345.0), "12.35k");
133        assert_eq!(humanize(1_234_567_890.0), "1.23G");
134    }
135}