use std::time::{Duration, Instant};
use anyhow::{bail, Context, Error};
#[derive(Debug, PartialEq, Eq)]
enum Timer
{
Running(Instant),
Finished(Duration),
}
#[derive(Debug, Default, PartialEq, Eq)]
pub struct Timers
{
timers: Vec<(String, Timer)>,
max_name_length: usize,
}
impl Timers
{
pub fn start(&mut self, name: &str) -> Result<(), Error>
{
if self.max_name_length < name.len()
{
self.max_name_length = name.len();
}
if self.timers.iter().any(|(other_name, _)| other_name == name)
{
bail!("Timer {name:?} already used");
}
self.timers.push((name.to_string(), Timer::Running(Instant::now())));
Ok(())
}
pub fn stop(&mut self, name: &str) -> Result<(), Error>
{
let index_of_timer = self
.timers
.iter()
.enumerate()
.find(|(_, (other_name, _))| other_name == name)
.with_context(|| format!("Timer {name} wasn't started yet"))?
.0;
match &self.timers[index_of_timer].1
{
Timer::Running(start) =>
{
self.timers[index_of_timer] = (name.to_string(), Timer::Finished(start.elapsed()));
}
Timer::Finished(time) => bail!("Timer with name {name} already finished with time {time:?}"),
}
Ok(())
}
pub fn print_all(&self)
{
println!();
println!();
println!();
for (name, timer) in &self.timers
{
let additional_space = " ".repeat(self.max_name_length - name.len());
match timer
{
Timer::Running(start) => println!(
"Timer {name:?} started {additional_space}{} seconds ago and is still running",
start.elapsed().as_secs_f64()
),
Timer::Finished(time) => println!(
"Timer {name:?} ran for {additional_space}{} seconds",
time.as_secs_f64()
),
}
}
println!();
println!();
println!();
}
}
#[cfg(test)]
mod tests
{
use super::Timers;
#[test]
fn timer_single_start()
{
Timers::default().start("abc").unwrap();
}
#[test]
fn timer_double_start()
{
let mut timers = Timers::default();
timers.start("def").unwrap();
assert!(timers.start("def").is_err());
assert!(timers.start("def").is_err());
assert!(timers.start("def").is_err());
timers.start("hij").unwrap();
}
#[test]
fn timer_nested_run()
{
let mut timers = Timers::default();
timers.start("a").unwrap();
timers.start("b").unwrap();
timers.stop("b").unwrap();
timers.stop("a").unwrap();
}
#[test]
fn timer_no_start()
{
assert!(Timers::default().stop("a").is_err());
}
#[test]
fn timer_reinit()
{
let mut timers = Timers::default();
timers.start("a").unwrap();
timers.start("b").unwrap();
timers.start("c").unwrap();
timers.stop("a").unwrap();
timers.stop("b").unwrap();
let mut timers = Timers::default();
timers.start("a").unwrap();
timers.start("b").unwrap();
timers.start("c").unwrap();
timers.stop("a").unwrap();
timers.stop("b").unwrap();
timers.stop("c").unwrap();
}
#[test]
#[should_panic = "called `Result::unwrap()` on an `Err` value: Timer c wasn't started yet"]
fn timer_reinit_fail()
{
let mut timers = Timers::default();
timers.start("a").unwrap();
timers.start("b").unwrap();
timers.start("c").unwrap();
timers.stop("a").unwrap();
timers.stop("b").unwrap();
let mut timers = Timers::default();
timers.stop("c").unwrap();
}
#[test]
#[should_panic = "called `Result::unwrap()` on an `Err` value: Timer \"a\" already used"]
fn timer_double()
{
let mut timers = Timers::default();
timers.start("a").unwrap();
timers.start("a").unwrap();
}
}