use std::time::{Duration, Instant};
#[derive(Debug)]
pub struct Timer {
label: String,
start: Instant,
log_on_drop: bool,
}
impl Timer {
pub fn start(label: impl Into<String>) -> Self {
Self {
label: label.into(),
start: Instant::now(),
log_on_drop: true,
}
}
pub fn silent() -> Self {
Self {
label: String::new(),
start: Instant::now(),
log_on_drop: false,
}
}
pub fn elapsed(&self) -> Duration {
self.start.elapsed()
}
pub fn elapsed_ms(&self) -> u64 {
self.elapsed().as_millis() as u64
}
pub fn elapsed_secs(&self) -> f64 {
self.elapsed().as_secs_f64()
}
pub fn stop(self) -> Duration {
let elapsed = self.elapsed();
if self.log_on_drop {
tracing::debug!(
"{} completed in {:.2}ms",
self.label,
elapsed.as_secs_f64() * 1000.0
);
}
elapsed
}
pub fn stop_ms(self) -> u64 {
self.stop().as_millis() as u64
}
pub fn silent_on_drop(mut self) -> Self {
self.log_on_drop = false;
self
}
pub fn reset(&mut self) {
self.start = Instant::now();
}
}
impl Drop for Timer {
fn drop(&mut self) {
if self.log_on_drop {
let elapsed = self.elapsed();
tracing::debug!(
"{} completed in {:.2}ms",
self.label,
elapsed.as_secs_f64() * 1000.0
);
}
}
}
pub fn format_duration(duration: Duration) -> String {
let total_ms = duration.as_millis();
if total_ms < 1000 {
format!("{}ms", total_ms)
} else if total_ms < 60_000 {
format!("{:.2}s", duration.as_secs_f64())
} else {
let secs = duration.as_secs();
let mins = secs / 60;
let remaining_secs = secs % 60;
format!("{}m {}s", mins, remaining_secs)
}
}
pub fn format_duration_compact(duration: Duration) -> String {
let total_ms = duration.as_millis();
if total_ms < 1000 {
format!("{}ms", total_ms)
} else if total_ms < 60_000 {
format!("{:.1}s", duration.as_secs_f64())
} else {
let mins = duration.as_secs() / 60;
let secs = duration.as_secs() % 60;
format!("{}:{:02}", mins, secs)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_timer_elapsed() {
let timer = Timer::silent();
std::thread::sleep(std::time::Duration::from_millis(10));
let elapsed = timer.elapsed();
assert!(elapsed.as_millis() >= 10);
}
#[test]
fn test_format_duration() {
assert_eq!(format_duration(Duration::from_millis(500)), "500ms");
assert_eq!(format_duration(Duration::from_millis(1500)), "1.50s");
assert_eq!(format_duration(Duration::from_secs(90)), "1m 30s");
}
#[test]
fn test_format_duration_compact() {
assert_eq!(format_duration_compact(Duration::from_millis(500)), "500ms");
assert_eq!(format_duration_compact(Duration::from_millis(1500)), "1.5s");
assert_eq!(format_duration_compact(Duration::from_secs(90)), "1:30");
}
}