floating_duration/lib.rs
1// Copyright 2017 Thomas Schaller.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! A small crate which allows combining
10//! a [`Duration`]'s seconds and nanoseconds
11//! into [seconds], [milliseconds] and [microseconds].
12//! Additionally, it allows [easy formatting] of a
13//! `Duration` for performance measurements.
14//!
15//! ## Conversion to fractional
16//!
17//! ```
18//! # #![allow(unused_variables)]
19//!
20//! use std::time::Duration;
21//! use floating_duration::TimeAsFloat;
22//!
23//! let duration = Duration::new(4, 123_456_789);
24//!
25//! let secs = duration.as_fractional_secs(); // 4.12..
26//! let millis = duration.as_fractional_millis(); // 4_123.45..
27//! let micros = duration.as_fractional_micros(); // 4_123_456.78..
28//! ```
29//!
30//! ## Automatic formatting
31//!
32//! ```
33//! use std::time::Instant;
34//! use floating_duration::TimeFormat;
35//!
36//! # fn do_something() {}
37//!
38//! # fn main() {
39//! let start = Instant::now();
40//!
41//! do_something();
42//!
43//! println!("Needed {}", TimeFormat(start.elapsed()));
44//! # }
45//! ```
46//!
47//! Output: `Needed 12.841µs`
48//!
49//! [`Duration`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html
50//! [seconds]: trait.TimeAsFloat.html#tymethod.as_fractional_secs
51//! [milliseconds]: trait.TimeAsFloat.html#tymethod.as_fractional_millis
52//! [microseconds]: trait.TimeAsFloat.html#tymethod.as_fractional_micros
53//!
54//! [easy formatting]: struct.TimeFormat.html
55
56use std::borrow::Borrow;
57use std::fmt::{Display, Error as FormatError, Formatter};
58use std::time::Duration;
59
60/// Trait for providing `as_fractional_*` methods.
61///
62/// # Examples
63///
64/// ## Measuring a time span
65///
66/// ```
67/// use std::time::Instant;
68/// use floating_duration::TimeAsFloat;
69///
70/// let start = Instant::now();
71///
72/// let result = (1..12).fold(1, |acc, x| acc * x);
73///
74/// println!("Needed {} seconds", start.elapsed().as_fractional_secs());
75/// // or:
76/// println!("Needed {:#}", start.elapsed().as_fractional_secs()); // uses the full unit name
77/// println!("Result: {}", result);
78/// ```
79pub trait TimeAsFloat {
80 /// Returns the duration in seconds.
81 fn as_fractional_secs(&self) -> f64;
82 /// Returns the duration in milliseconds.
83 fn as_fractional_millis(&self) -> f64;
84 /// Returns the duration in microseconds.
85 fn as_fractional_micros(&self) -> f64;
86}
87
88impl<T: Borrow<Duration>> TimeAsFloat for T {
89 fn as_fractional_secs(&self) -> f64 {
90 let dur: &Duration = self.borrow();
91
92 dur.as_secs() as f64 + dur.subsec_nanos() as f64 / 1_000_000_000.0
93 }
94
95 fn as_fractional_millis(&self) -> f64 {
96 let dur: &Duration = self.borrow();
97
98 dur.as_secs() as f64 * 1_000.0 + dur.subsec_nanos() as f64 / 1_000_000.0
99 }
100
101 fn as_fractional_micros(&self) -> f64 {
102 let dur: &Duration = self.borrow();
103
104 dur.as_secs() as f64 * 1_000_000.0 + dur.subsec_nanos() as f64 / 1_000.0
105 }
106}
107
108/// A formatting newtype for providing a
109/// [`Display`] implementation. This format is
110/// meant to be used for printing performance measurements.
111///
112/// # Behaviour
113///
114/// * `secs > 0` => seconds with precision 3
115/// * `secs > 0.001` => milliseconds with precision 3
116/// * `secs > 0.000_001` => microseconds with precision 3
117/// * otherwise => nanoseconds
118///
119/// By default the duration is formatted using abbreviated units
120/// (e.g. `1.234ms`).
121/// If the the format string is specified with the [alternate flag] `{:#}`,
122/// the duration is formatted using the full unit name instead
123/// (e.g. `1.234 milliseconds`).
124///
125/// # Examples
126///
127/// ```
128/// use std::time::Duration;
129/// use floating_duration::TimeFormat;
130///
131/// let dur = Duration::new(0, 461_933);
132/// let formatted = format!("{}", TimeFormat(dur));
133/// assert_eq!(formatted, "461.933µs");
134/// let alternate = format!("{:#}", TimeFormat(dur));
135/// assert_eq!(alternate, "461.933 microseconds");
136/// ```
137///
138/// [`Display`]: https://doc.rust-lang.org/stable/std/fmt/trait.Display.html
139/// [alternate flag]: https://doc.rust-lang.org/stable/std/fmt/#sign0
140#[derive(Clone, Copy, Debug)]
141pub struct TimeFormat<T: Borrow<Duration>>(pub T);
142
143impl<T: Borrow<Duration>> Display for TimeFormat<T> {
144 fn fmt(&self, f: &mut Formatter) -> Result<(), FormatError> {
145 let dur: &Duration = self.0.borrow();
146
147 if dur.as_secs() > 0 {
148 if !f.alternate() {
149 write!(f, "{:.3}s", dur.as_fractional_secs())
150 } else {
151 write!(f, "{:.3} seconds", dur.as_fractional_secs())
152 }
153 } else if dur.subsec_nanos() > 1_000_000 {
154 if !f.alternate() {
155 write!(f, "{:.3}ms", dur.as_fractional_millis())
156 } else {
157 write!(f, "{:.3} milliseconds", dur.as_fractional_millis())
158 }
159 } else if dur.subsec_nanos() > 1_000 {
160 if !f.alternate() {
161 write!(f, "{:.3}µs", dur.as_fractional_micros())
162 } else {
163 write!(f, "{:.3} microseconds", dur.as_fractional_micros())
164 }
165 } else {
166 if !f.alternate() {
167 write!(f, "{}ns", dur.subsec_nanos())
168 } else {
169 write!(f, "{} nanoseconds", dur.subsec_nanos())
170 }
171 }
172 }
173}