1use std::time::{Duration, Instant};
2
3#[allow(dead_code)]
4fn format_duration(nanos: f64) -> String {
5 if nanos < 1_000.0 {
6 format!("{nanos:.2} ns")
7 } else if nanos < 100_000.0 {
8 format!("{:.2} μs", nanos / 1_000.0)
9 } else if nanos < 1_000_000_000.0 {
10 format!("{:.2} ms", nanos / 1_000_000.0)
11 } else {
12 format!("{:.2} s", nanos / 1_000_000_000.0)
13 }
14}
15
16pub enum Grade {
17 Great,
18 Good,
19 Warning,
20 Bad,
21}
22
23fn grade_from_nano(nanos: f64) -> Grade {
24 let ms = (nanos / 1_000_000.0) as u64;
25 if ms < 10 {
26 Grade::Great
27 } else if ms < 40 {
28 Grade::Good
29 } else if ms < 70 {
30 Grade::Warning
31 } else {
32 Grade::Bad
33 }
34}
35
36const RESET: &str = "\x1B[0m";
37const RED: &str = "\x1B[31m";
38const GREEN: &str = "\x1B[32m";
39const YELLOW_BOLD: &str = "\x1B[1;33m";
40const CYAN: &str = "\x1B[36m";
41const BLUE: &str = "\x1B[34m";
42
43#[cfg(feature = "enable_summary")]
44pub fn summary(name: &str, total_execution_time: Duration) {
45 let duration = total_execution_time;
46 let total_nanos = duration.as_nanos() as f64;
47
48 let grade = grade_from_nano(total_nanos);
49 let grade_color_string = match grade {
50 Grade::Great => GREEN,
51 Grade::Good => CYAN,
52 Grade::Warning => YELLOW_BOLD,
53 Grade::Bad => RED,
54 };
55
56 println!(
57 "timer '{}{}{}' completed in {}{}{}",
58 BLUE,
59 name,
60 RESET,
61 grade_color_string,
62 format_duration(total_nanos),
63 RESET,
64 );
65}
66
67#[derive(Debug)]
72pub struct ScopedTimer<'a> {
73 start: Instant,
74 #[allow(dead_code)]
75 name: &'a str,
76}
77
78impl<'a> ScopedTimer<'a> {
79 #[must_use]
85 pub fn new(name: &'a str) -> Self {
86 Self {
87 start: Instant::now(),
88 name,
89 }
90 }
91
92 #[must_use]
94 pub fn elapsed(&self) -> Duration {
95 self.start.elapsed()
96 }
97}
98
99impl Drop for ScopedTimer<'_> {
100 fn drop(&mut self) {
101 #[cfg(feature = "enable_summary")]
102 {
103 let elapsed = self.start.elapsed();
104 summary(self.name, elapsed);
105 }
106 }
107}