use std::time::{Duration, Instant};
pub struct Timer {
time_started: Instant,
duration: Duration,
paused: bool,
}
impl Timer {
pub fn new(start_paused: bool) -> Self {
Self {
time_started: Instant::now(),
duration: Duration::ZERO,
paused: start_paused,
}
}
pub fn restart(&mut self) {
self.paused = false;
self.duration = Duration::ZERO;
self.time_started = Instant::now();
}
pub fn resume(&mut self) {
if !self.paused {
return;
}
self.paused = false;
self.time_started = Instant::now();
}
pub fn pause(&mut self) {
if self.paused {
return;
}
self.duration += self.time_started.elapsed();
self.paused = true;
}
pub fn elapsed(&self) -> Duration {
if self.paused {
self.duration
} else {
self.duration + self.time_started.elapsed()
}
}
pub fn elapsed_nanos(&self) -> u128 {
self.elapsed().as_nanos()
}
pub fn format_duration(dur: Duration) -> String {
let nanos = dur.as_nanos() as f64;
if nanos < 1.0 {
return "0 microsecs".to_string();
}
let log10 = nanos.log10() as i32;
if log10 < 6 {
let precision = (2 - (log10 % 3)) as usize;
format!("{:.prec$} microsecs", nanos * 1.0e-3, prec = precision)
} else if log10 < 9 {
let precision = (2 - (log10 % 3)) as usize;
format!("{:.prec$} millisecs", nanos * 1.0e-6, prec = precision)
} else {
let precision = (2 - (log10 % 3)) as usize;
format!("{:.prec$} secs", nanos * 1.0e-9, prec = precision)
}
}
pub fn elapsed_str(&self) -> String {
Self::format_duration(self.elapsed())
}
}
impl Default for Timer {
fn default() -> Self {
Self::new(false)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
#[test]
fn test_timer_starts_running() {
let timer = Timer::new(false);
thread::sleep(Duration::from_millis(10));
assert!(timer.elapsed() >= Duration::from_millis(5));
}
#[test]
fn test_timer_starts_paused() {
let timer = Timer::new(true);
thread::sleep(Duration::from_millis(10));
assert!(timer.elapsed() < Duration::from_millis(1));
}
#[test]
fn test_timer_pause_resume() {
let mut timer = Timer::new(false);
thread::sleep(Duration::from_millis(20));
timer.pause();
let paused_elapsed = timer.elapsed();
thread::sleep(Duration::from_millis(20));
assert_eq!(timer.elapsed(), paused_elapsed);
timer.resume();
thread::sleep(Duration::from_millis(20));
assert!(timer.elapsed() > paused_elapsed);
}
#[test]
fn test_timer_restart() {
let mut timer = Timer::new(false);
thread::sleep(Duration::from_millis(20));
timer.restart();
assert!(timer.elapsed() < Duration::from_millis(5));
}
#[test]
fn test_format_duration_microsecs() {
let s = Timer::format_duration(Duration::from_micros(500));
assert!(s.contains("microsecs"));
}
#[test]
fn test_format_duration_millisecs() {
let s = Timer::format_duration(Duration::from_millis(50));
assert!(s.contains("millisecs"));
}
#[test]
fn test_format_duration_secs() {
let s = Timer::format_duration(Duration::from_secs(2));
assert!(s.contains("secs"));
}
#[test]
fn test_format_duration_zero() {
let s = Timer::format_duration(Duration::ZERO);
assert!(s.contains("microsecs"));
}
#[test]
fn test_default_timer_starts_running() {
let timer = Timer::default();
thread::sleep(Duration::from_millis(10));
assert!(timer.elapsed() >= Duration::from_millis(5));
}
#[test]
fn test_elapsed_nanos() {
let timer = Timer::new(false);
thread::sleep(Duration::from_millis(10));
assert!(timer.elapsed_nanos() > 1_000_000); }
}