1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use std::fmt;
use std::time::{Duration, Instant};

/// Simple struct tracking elapsed time.
///
/// On creation this struct immediately starts tracking elapsed time.
#[derive(Clone, Debug)]
pub struct Timer {
    /// The instant this stopwatch was started at.
    start: Instant,
}

impl Timer {
    /// Construct a new `Timer` stopwatch, start immediately.
    pub fn new() -> Self {
        Self::default()
    }

    /// Get the elapsed time.
    pub fn took(&self) -> Took {
        Took::from_std(self.start.elapsed())
    }
}

impl Default for Timer {
    fn default() -> Self {
        Self {
            start: Instant::now(),
        }
    }
}

impl fmt::Display for Timer {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.took().fmt(f)
    }
}

/// Defines elasped time.
#[derive(Clone, Debug)]
pub struct Took {
    /// Elapsed time as duration.
    elapsed: Duration,
}

impl Took {
    /// Construct `Took` from `Duration` in `std`.
    pub fn from_std(elapsed: Duration) -> Self {
        Self { elapsed }
    }

    /// Extract the inner `Duration`.
    pub fn into_std(self) -> Duration {
        self.elapsed
    }

    /// Get a reference to the inner `Duration`.
    pub fn as_std(&self) -> &Duration {
        &self.elapsed
    }

    /// Print the elapsed time with a description.
    ///
    /// Prints the following formatted text to `stdout`:
    ///
    /// ```skip
    /// description took 0 s
    /// ```
    pub fn describe(&self, description: &str) {
        println!("{} took {}", description, self);
    }
}

impl fmt::Display for Took {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let elapsed = self.elapsed;
        let secs = u128::from(elapsed.as_secs());
        let millis = elapsed.as_millis();
        let micros = elapsed.as_micros();
        let nanos = elapsed.as_nanos();

        let (major, minor, t) = if secs > 0 {
            (secs, millis, "s")
        } else if millis > 0 {
            (millis, micros, "ms")
        } else if micros > 0 {
            (micros, nanos, "μs")
        } else {
            (nanos, nanos * 1000, "ns")
        };

        let time = major as f64 + (minor - major * 1000) as f64 / 1000.0;
        f.pad(&format!("{:.2} {}", time, t))
    }
}

/// Measure run time of given function, return elapsed time.
///
/// Returns `Took` along with the function result.
#[must_use]
pub fn took<T, F>(f: F) -> (Took, T)
where
    F: FnOnce() -> T,
{
    let take = Timer::new();
    let out = f();
    (take.took(), out)
}

/// Measure run time of given function, print elapsed time.
///
/// Prints the following formatted text to `stdout`:
///
/// ```skip
/// description took 0 s
/// ```
///
/// Returns the function result.
#[must_use]
pub fn took_print<T, F>(description: &str, f: F) -> T
where
    F: FnOnce() -> T,
{
    let (took, out) = took(f);
    eprintln!("{} took {}", description, took);
    out
}