use std::{
fmt,
time::{Duration, Instant},
};
#[derive(Default)]
pub struct Timer {
names: Vec<&'static str>,
durations: Vec<Duration>,
started: Option<Instant>,
}
impl Timer {
pub fn new() -> Self {
Default::default()
}
pub fn start(&mut self, name: &'static str) {
self.finish();
self.names.push(name);
self.started = Some(Instant::now());
}
pub fn finish(&mut self) -> &Self {
if let Some(started) = self.started {
self.durations.push(started.elapsed());
self.started = None;
}
self
}
pub fn print(&mut self) {
self.finish();
println!("{self}");
}
fn max_name_len(&self) -> usize {
self.names.iter().map(|x| x.len()).max().unwrap_or_default()
}
fn max_duration_len(&self) -> usize {
self.durations
.iter()
.max()
.unwrap_or(&Duration::ZERO)
.as_nanos()
.to_string()
.len()
}
fn total_duration(&self) -> Duration {
self.durations.iter().sum()
}
}
impl fmt::Display for Timer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let max_name = self.max_name_len();
let max_duration = self.max_duration_len();
let total_duration = self.total_duration().as_nanos();
let mut sorted_durations: Vec<_> = self.names.iter().zip(&self.durations).collect();
sorted_durations.sort_by(|a, b| b.1.cmp(a.1));
for (name, duration) in sorted_durations {
let d = duration.as_nanos();
let percent = d * 100 / total_duration;
writeln!(
f,
"{:>max_name$} | {:>max_duration$} ns | {:>3}%",
name, d, percent
)?;
}
if self.started.is_some() {
writeln!(
f,
"WARNING: timer event `{}` has not been finished properly, run `Timer::finish()`",
self.names.iter().last().unwrap_or(&"")
)?;
}
Ok(())
}
}