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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
extern crate chrono;

/// This macro measures the execution time of an expression,
/// then returns a (result, duration) tuple where:
/// - `result` is the result of executing the expression on its own
/// - `duration` is a chrono::Duration.
#[macro_export]
macro_rules! measure {
    ($e:expr) => {{
        use chrono::{Utc, Duration};
        let pre = Utc::now();
        let result = { $e };
        let delta: Duration = Utc::now().signed_duration_since(pre);
        (result,  delta)
    }}
}


/// A trait to display duration outputs of the
/// `measurement!` macro in a human-readable way.
pub trait MeasureDisplay {
    fn human_readable(&self) -> String;
}

impl MeasureDisplay for chrono::Duration {
    fn human_readable(&self) -> String {
        if self.num_nanoseconds().is_none() {
            return String::from("overflow");
        }

        const NS_PER_US: u64   = 1e3 as u64;
        const NS_PER_MS: u64   = 1e6 as u64;
        const NS_PER_SEC: u64  = 1e9 as u64;
        const NS_PER_MIN: u64  = 60 * NS_PER_SEC;
        const NS_PER_HOUR: u64 = 60 * NS_PER_MIN;

        match self.num_nanoseconds().unwrap() as u64 {
            nanos if nanos < NS_PER_US => format!("{} ns", nanos),
            nanos if nanos < NS_PER_MS => {
                let micros: u64 = nanos / NS_PER_US;
                let nanos: u64 = nanos % NS_PER_US;
                if nanos > 0 {
                    format!("{} µs {} ns", micros, nanos)
                } else {
                    format!("{} µs", micros)
                }
            },
            nanos if nanos < NS_PER_SEC => {
                let millis: u64 = nanos / NS_PER_MS;
                let micros: u64 = (nanos % NS_PER_MS) / NS_PER_US;
                if micros > 0 {
                    format!("{} ms {} µs", millis, micros)
                } else {
                    format!("{} ms", millis)
                }
            },
            nanos if nanos < NS_PER_MIN => {
                let secs: u64 = nanos / NS_PER_SEC;
                let millis: u64 = (nanos % NS_PER_SEC) / NS_PER_MS;
                if millis > 0 {
                    format!("{} s {} ms", secs, millis)
                } else {
                    format!("{} s", secs)
                }
            },
            nanos if nanos < NS_PER_HOUR => {
                let mins: u64 = nanos / NS_PER_MIN;
                let secs: u64 = (nanos % NS_PER_MIN) / NS_PER_SEC;
                if secs > 0 {
                    format!("{} m {} s", mins, secs)
                } else {
                    format!("{} m", mins)
                }
            },
            nanos => {
                let hours: u64 = nanos / NS_PER_HOUR;
                let mins: u64 = (nanos % NS_PER_HOUR) / NS_PER_MIN;
                if mins > 0 {
                    format!("{} h {} m", hours, mins)
                } else {
                    format!("{} h", hours)
                }
            },
        }
    }
}


#[cfg(test)]
mod tests {
    use chrono::Duration;
    use ::MeasureDisplay;

    #[test]
    fn human_readable_hours() {
        let foo = Duration::hours(10);
        assert_eq!("10 h", foo.human_readable());

        let bar = Duration::hours(3)
            .checked_add(&Duration::minutes(3))
            .unwrap();
        assert_eq!("3 h 3 m", bar.human_readable());
    }

    #[test]
    fn human_readable_minutes() {
        let foo = Duration::minutes(10);
        assert_eq!("10 m", foo.human_readable());

        let bar = Duration::minutes(3)
            .checked_add(&Duration::seconds(3))
            .unwrap();
        assert_eq!("3 m 3 s", bar.human_readable());
    }

    #[test]
    fn human_readable_seconds() {
        let foo = Duration::seconds(10);
        assert_eq!("10 s", foo.human_readable());

        let bar = Duration::seconds(3)
            .checked_add(&Duration::milliseconds(3))
            .unwrap();
        assert_eq!("3 s 3 ms", bar.human_readable());
    }

    #[test]
    fn human_readable_milliseconds() {
        let foo = Duration::milliseconds(10);
        assert_eq!("10 ms", foo.human_readable());

        let bar = Duration::milliseconds(3)
            .checked_add(&Duration::microseconds(3))
            .unwrap();
        assert_eq!("3 ms 3 µs", bar.human_readable());
    }

    #[test]
    fn human_readable_microseconds() {
        let foo = Duration::microseconds(10);
        assert_eq!("10 µs", foo.human_readable());

        let bar = Duration::microseconds(3)
            .checked_add(&Duration::nanoseconds(3))
            .unwrap();
        assert_eq!("3 µs 3 ns", bar.human_readable());
    }

    #[test]
    fn human_readable_nanoseconds() {
        let foo = Duration::nanoseconds(10);
        assert_eq!("10 ns", foo.human_readable());
    }
}