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
// Copyright 2017 Thomas Schaller.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A small crate which allows combining
//! a [`Duration`]'s seconds and nanoseconds
//! into [seconds], [milliseconds] and [microseconds].
//! Additionally, it allows [easy formatting] of a
//! `Duration` for performance measurements.
//!
//! ## Conversion to fractional
//!
//! ```
//! # #![allow(unused_variables)]
//!
//! use std::time::Duration;
//! use floating_duration::TimeAsFloat;
//!
//! let duration = Duration::new(4, 123_456_789);
//!
//! let secs = duration.as_fractional_secs(); // 4.12..
//! let millis = duration.as_fractional_millis(); // 4_123.45..
//! let micros = duration.as_fractional_micros(); // 4_123_456.78..
//! ```
//!
//! ## Automatic formatting
//!
//! ```
//! use std::time::Instant;
//! use floating_duration::TimeFormat;
//!
//! # fn do_something() {}
//!
//! # fn main() {
//! let start = Instant::now();
//!
//! do_something();
//!
//! println!("Needed {}", TimeFormat(start.elapsed()));
//! # }
//! ```
//!
//! Output: `Needed 12.841 microseconds`
//!
//! [`Duration`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html
//! [seconds]: trait.TimeAsFloat.html#tymethod.as_fractional_secs
//! [milliseconds]: trait.TimeAsFloat.html#tymethod.as_fractional_millis
//! [microseconds]: trait.TimeAsFloat.html#tymethod.as_fractional_micros
//!
//! [easy formatting]: struct.TimeFormat.html

use std::borrow::Borrow;
use std::fmt::{Display, Error as FormatError, Formatter};
use std::time::Duration;

/// Trait for providing `as_fractional_*` methods.
///
/// # Examples
///
/// ## Measuring a time span
///
/// ```
/// use std::time::Instant;
/// use floating_duration::TimeAsFloat;
///
/// let start = Instant::now();
///
/// let result = (1..12).fold(1, |acc, x| acc * x);
///
/// println!("Needed {} seconds", start.elapsed().as_fractional_secs());
/// println!("Result: {}", result);
/// ```
pub trait TimeAsFloat {
    /// Returns the duration in seconds.
    fn as_fractional_secs(&self) -> f64;
    /// Returns the duration in milliseconds.
    fn as_fractional_millis(&self) -> f64;
    /// Returns the duration in microseconds.
    fn as_fractional_micros(&self) -> f64;
}

impl<T: Borrow<Duration>> TimeAsFloat for T {
    fn as_fractional_secs(&self) -> f64 {
        let dur: &Duration = self.borrow();

        dur.as_secs() as f64 + dur.subsec_nanos() as f64 / 1_000_000_000.0
    }

    fn as_fractional_millis(&self) -> f64 {
        let dur: &Duration = self.borrow();

        dur.as_secs() as f64 * 1_000.0 + dur.subsec_nanos() as f64 / 1_000_000.0
    }

    fn as_fractional_micros(&self) -> f64 {
        let dur: &Duration = self.borrow();

        dur.as_secs() as f64 * 1_000_000.0 + dur.subsec_nanos() as f64 / 1_000.0
    }
}

/// A formatting newtype for providing a
/// [`Display`] implementation. This format is
/// meant to be used for printing performance measurements.
///
/// # Behaviour
///
/// * `secs > 0` => seconds with precision 3
/// * `secs > 0.001` => milliseconds with precision 3
/// * `secs > 0.000_001` => microseconds with precision 3
/// * otherwise => nanoseconds
///
/// # Examples
///
/// ```
/// use std::time::Duration;
/// use floating_duration::TimeFormat;
///
/// let dur = Duration::new(0, 461_933);
/// let formatted = format!("{}", TimeFormat(dur));
/// assert_eq!(formatted, "461.933 microseconds");
/// ```
///
/// [`Display`]: https://doc.rust-lang.org/stable/std/fmt/trait.Display.html
#[derive(Clone, Copy, Debug)]
pub struct TimeFormat<T: Borrow<Duration>>(pub T);

impl<T: Borrow<Duration>> Display for TimeFormat<T> {
    fn fmt(&self, f: &mut Formatter) -> Result<(), FormatError> {
        let dur: &Duration = self.0.borrow();

        if dur.as_secs() > 0 {
            write!(f, "{:.3} seconds", dur.as_fractional_secs())
        } else if dur.subsec_nanos() > 1_000_000 {
            write!(f, "{:.3} milliseconds", dur.as_fractional_millis())
        } else if dur.subsec_nanos() > 1_000 {
            write!(f, "{:.3} microseconds", dur.as_fractional_micros())
        } else {
            write!(f, "{} nanoseconds", dur.subsec_nanos())
        }
    }
}