Skip to main content

qubit_clock/meter/
format.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! Duration and speed formatting utilities.
10//!
11//! This module provides functions to format durations and speeds into
12//! human-readable strings.
13//!
14//! # Author
15//!
16//! Haixing Hu
17
18/// Formats a duration in milliseconds into a human-readable string.
19///
20/// The format adapts based on the duration:
21/// - Less than 1 second: "X ms"
22/// - Less than 1 minute: "X.Ys" (rounded to 1 decimal place)
23/// - Less than 1 hour: "Xm Ys"
24/// - 1 hour or more: "Xh Ym Zs"
25///
26/// # Arguments
27///
28/// * `millis` - The duration in milliseconds
29///
30/// # Returns
31///
32/// A human-readable string representation of the duration
33///
34/// # Examples
35///
36/// ```
37/// use qubit_clock::meter::format_duration_millis;
38///
39/// assert_eq!(format_duration_millis(500), "500 ms");
40/// assert_eq!(format_duration_millis(1500), "1.5s");
41/// assert_eq!(format_duration_millis(65000), "1m 5s");
42/// assert_eq!(format_duration_millis(3665000), "1h 1m 5s");
43/// ```
44pub fn format_duration_millis(millis: i64) -> String {
45    if millis < 0 {
46        return "0 ms".to_string();
47    }
48
49    if millis < 1000 {
50        return format!("{} ms", millis);
51    }
52
53    let total_seconds = millis / 1000;
54    let hours = total_seconds / 3600;
55    let minutes = (total_seconds % 3600) / 60;
56    let seconds = total_seconds % 60;
57
58    if hours > 0 {
59        if minutes > 0 && seconds > 0 {
60            format!("{}h {}m {}s", hours, minutes, seconds)
61        } else if minutes > 0 {
62            format!("{}h {}m", hours, minutes)
63        } else if seconds > 0 {
64            format!("{}h {}s", hours, seconds)
65        } else {
66            format!("{}h", hours)
67        }
68    } else if minutes > 0 {
69        if seconds > 0 {
70            format!("{}m {}s", minutes, seconds)
71        } else {
72            format!("{}m", minutes)
73        }
74    } else {
75        let millis_part = millis % 1000;
76        if millis_part > 0 {
77            format!("{}.{}s", seconds, millis_part / 100)
78        } else {
79            format!("{}s", seconds)
80        }
81    }
82}
83
84/// Formats a duration in nanoseconds into a human-readable string.
85///
86/// The format adapts based on the duration:
87/// - Less than 1 microsecond: "X ns"
88/// - Less than 1 millisecond: "X.Y μs" (rounded to 1 decimal place)
89/// - Less than 1 second: "X.Y ms" (rounded to 1 decimal place)
90/// - 1 second or more: delegates to `format_duration_millis`
91///
92/// # Arguments
93///
94/// * `nanos` - The duration in nanoseconds
95///
96/// # Returns
97///
98/// A human-readable string representation of the duration
99///
100/// # Examples
101///
102/// ```
103/// use qubit_clock::meter::format_duration_nanos;
104///
105/// assert_eq!(format_duration_nanos(500), "500 ns");
106/// assert_eq!(format_duration_nanos(1500), "1.5 μs");
107/// assert_eq!(format_duration_nanos(1500000), "1.5 ms");
108/// assert_eq!(format_duration_nanos(1500000000), "1.5s");
109/// ```
110pub fn format_duration_nanos(nanos: i128) -> String {
111    if nanos < 0 {
112        return "0 ns".to_string();
113    }
114
115    if nanos < 1000 {
116        return format!("{} ns", nanos);
117    }
118
119    if nanos < 1_000_000 {
120        let micros = nanos / 1000;
121        let nanos_part = nanos % 1000;
122        if nanos_part > 0 {
123            format!("{}.{} μs", micros, nanos_part / 100)
124        } else {
125            format!("{} μs", micros)
126        }
127    } else if nanos < 1_000_000_000 {
128        let millis = nanos / 1_000_000;
129        let micros_part = (nanos % 1_000_000) / 100_000;
130        if micros_part > 0 {
131            format!("{}.{} ms", millis, micros_part)
132        } else {
133            format!("{} ms", millis)
134        }
135    } else {
136        let millis = (nanos / 1_000_000) as i64;
137        format_duration_millis(millis)
138    }
139}
140
141/// Formats a speed value with a unit suffix.
142///
143/// The speed is formatted with 2 decimal places. If the speed is NaN or
144/// infinite, returns "N/A".
145///
146/// # Arguments
147///
148/// * `speed` - The speed value
149/// * `unit` - The unit suffix (e.g., "/s", "/m")
150///
151/// # Returns
152///
153/// A formatted string like "123.45/s" or "N/A"
154///
155/// # Examples
156///
157/// ```
158/// use qubit_clock::meter::format_speed;
159///
160/// assert_eq!(format_speed(123.456, "/s"), "123.46/s");
161/// assert_eq!(format_speed(0.0, "/m"), "0.00/m");
162/// assert_eq!(format_speed(f64::NAN, "/s"), "N/A");
163/// ```
164pub fn format_speed(speed: f64, unit: &str) -> String {
165    if speed.is_nan() || speed.is_infinite() {
166        "N/A".to_string()
167    } else {
168        format!("{:.2}{}", speed, unit)
169    }
170}