1
#![doc = include_str!("../README.md")]
2
// @@ begin lint list maintained by maint/add_warning @@
3
#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
4
#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
5
#![warn(missing_docs)]
6
#![warn(noop_method_call)]
7
#![warn(unreachable_pub)]
8
#![warn(clippy::all)]
9
#![deny(clippy::await_holding_lock)]
10
#![deny(clippy::cargo_common_metadata)]
11
#![deny(clippy::cast_lossless)]
12
#![deny(clippy::checked_conversions)]
13
#![allow(clippy::cognitive_complexity)] // See arti#2556
14
#![deny(clippy::debug_assert_with_mut_call)]
15
#![deny(clippy::exhaustive_enums)]
16
#![deny(clippy::exhaustive_structs)]
17
#![deny(clippy::expl_impl_clone_on_copy)]
18
#![deny(clippy::fallible_impl_from)]
19
#![deny(clippy::implicit_clone)]
20
#![deny(clippy::large_stack_arrays)]
21
#![warn(clippy::manual_ok_or)]
22
#![deny(clippy::missing_docs_in_private_items)]
23
#![warn(clippy::needless_borrow)]
24
#![warn(clippy::needless_pass_by_value)]
25
#![warn(clippy::option_option)]
26
#![warn(clippy::or_fun_call)]
27
#![deny(clippy::print_stderr)]
28
#![deny(clippy::print_stdout)]
29
#![warn(clippy::rc_buffer)]
30
#![deny(clippy::ref_option_ref)]
31
#![warn(clippy::semicolon_if_nothing_returned)]
32
#![warn(clippy::trait_duplication_in_bounds)]
33
#![deny(clippy::unchecked_time_subtraction)]
34
#![deny(clippy::unnecessary_wraps)]
35
#![warn(clippy::unseparated_literal_suffix)]
36
#![deny(clippy::unwrap_used)]
37
#![deny(clippy::mod_module_files)]
38
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
39
#![allow(clippy::uninlined_format_args)]
40
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
41
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
42
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
43
#![allow(clippy::needless_lifetimes)] // See arti#1765
44
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
45
#![allow(clippy::collapsible_if)] // See arti#2342
46
#![deny(clippy::unused_async)]
47
#![deny(clippy::string_slice)] // See arti#2571
48
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
49
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
50
#![allow(unexpected_cfgs)]
51
#![forbid(unsafe_code)]
52
#![cfg_attr(
53
    saturating_time_nightly,
54
    feature(time_systemtime_limits, time_saturating_systemtime)
55
)]
56

            
57
use std::time::{Duration, Instant, SystemTime};
58

            
59
mod internal;
60

            
61
/// The core trait of this crait, [`SaturatingTime`].
62
///
63
/// This trait provides methods for performing saturating arithmetic on those
64
/// types in [`std::time`] that not already come with such a functionality,
65
/// such as [`SystemTime`] or [`Instant`].
66
///
67
/// The trait itself is not implementable from the outside, because it is sealed
68
/// by an internal trait.
69
///
70
/// See the methods or the top-level documentation for concrete code examples.
71
pub trait SaturatingTime: internal::SaturatingTime {
72
    /// Returns the maximum value for this type on the current platform.
73
    ///
74
    /// This limit is highly platform specific.  It differs heavily between
75
    /// Unix, Windows, and other operating systems.
76
    ///
77
    /// The limit itself is calculated dynamically during runtime with a correct
78
    /// algorithm.  Afterwards, it gets stored in a lazy static value, meaning
79
    /// that only the first call to it will be slightly more expensive, whereas
80
    /// all latter calls will result in an immediate return of the value.
81
    ///
82
    /// # Examples
83
    ///
84
    /// ```
85
    /// use std::time::{Duration, SystemTime};
86
    /// use saturating_time::SaturatingTime;
87
    ///
88
    /// let max = SystemTime::max_value();
89
    ///
90
    /// // Adding zero to the maximum value will change nothing.
91
    /// assert!(max.checked_add(Duration::ZERO).is_some());
92
    ///
93
    /// // Adding 1ns to the maximum value will fail.
94
    /// assert!(max.checked_add(Duration::new(0, 1)).is_none());
95
    ///
96
    /// // Subtracting 1ns from the maximum value will work of course.
97
    /// assert!(max.checked_sub(Duration::new(0, 1)).is_some());
98
    /// ```
99
8
    fn max_value() -> Self {
100
8
        internal::SaturatingTime::max_value()
101
8
    }
102

            
103
    /// Returns the minimum value for this type on the current platform.
104
    ///
105
    /// This limit is highly platform specific.  It differs heavily between
106
    /// Unix, Windows, and other operating systems.
107
    ///
108
    /// The limit itself is calculated dynamically during runtime with a correct
109
    /// algorithm.  Afterwards, it gets stored in a lazy static value, meaning
110
    /// that only the first call to it will be slightly more expensive, whereas
111
    /// all latter calls will result in an immediate return of the value.
112
    ///
113
    /// # Examples
114
    ///
115
    /// ```
116
    /// use std::time::{Duration, SystemTime};
117
    /// use saturating_time::SaturatingTime;
118
    ///
119
    /// let min = SystemTime::min_value();
120
    ///
121
    /// // Subtracting a zero from the minimum value will change nothing.
122
    /// assert!(min.checked_sub(Duration::ZERO).is_some());
123
    ///
124
    /// // Subtracting 1ns from the minimum value will fail.
125
    /// assert!(min.checked_sub(Duration::new(0, 1)).is_none());
126
    ///
127
    /// // Adding 1ns to the minimum value will work of course.
128
    /// assert!(min.checked_add(Duration::new(0, 1)).is_some());
129
    /// ```
130
8
    fn min_value() -> Self {
131
8
        internal::SaturatingTime::min_value()
132
8
    }
133

            
134
    /// Performs a saturating addition of a [`Duration`].
135
    ///
136
    /// The resulting value will saturate to [`SaturatingTime::max_value()`] in
137
    /// the case the addition would have caused an overflow of value.
138
    ///
139
    /// # Examples
140
    ///
141
    /// ```
142
    /// use std::time::{Duration, SystemTime};
143
    /// use saturating_time::SaturatingTime;
144
    ///
145
    /// let max = SystemTime::max_value();
146
    ///
147
    /// // Adding zero will change nothing.
148
    /// assert_eq!(max.saturating_add(Duration::ZERO), max);
149
    ///
150
    /// // Adding 1ns would overflow so we saturate to the maximum.
151
    /// assert_eq!(max.saturating_add(Duration::new(0, 1)), max);
152
    /// ```
153
538863
    fn saturating_add(self, duration: Duration) -> Self {
154
538863
        self.checked_add(duration)
155
538863
            .unwrap_or_else(SaturatingTime::max_value)
156
538863
    }
157

            
158
    /// Performs a saturating subtraction of a [`Duration`].
159
    ///
160
    /// The resulting value will saturate to [`SaturatingTime::min_value()`] in
161
    /// the case the subtraction would have caused an overflow of value.
162
    ///
163
    /// # Examples
164
    ///
165
    /// ```
166
    /// use std::time::{Duration, SystemTime};
167
    /// use saturating_time::SaturatingTime;
168
    ///
169
    /// let min = SystemTime::min_value();
170
    ///
171
    /// // Subtracting zero will change nothing.
172
    /// assert_eq!(min.saturating_sub(Duration::ZERO), min);
173
    ///
174
    /// // Subtracting 1ns would overflow so we saturate to the minimum.
175
    /// assert_eq!(min.saturating_sub(Duration::new(0, 1)), min);
176
    /// ```
177
2828
    fn saturating_sub(self, duration: Duration) -> Self {
178
2828
        self.checked_sub(duration)
179
2828
            .unwrap_or_else(SaturatingTime::min_value)
180
2828
    }
181

            
182
    /// Performs a saturating time difference calculation between two points.
183
    ///
184
    /// The resulting value will saturate to [`Duration::ZERO`] in the case that
185
    /// the `earlier` point in time is actually not earlier, thereby resulting
186
    /// in a negative difference.
187
    ///
188
    /// # Examples
189
    ///
190
    /// ```
191
    /// use std::time::{Duration, SystemTime};
192
    /// use saturating_time::SaturatingTime;
193
    ///
194
    /// let epoch = SystemTime::UNIX_EPOCH;
195
    /// let now = SystemTime::now();
196
    /// let min = SystemTime::min_value();
197
    ///
198
    /// assert!(now.saturating_duration_since(epoch).as_secs() > 0);
199
    /// assert!(epoch.saturating_duration_since(epoch) == Duration::ZERO);
200
    /// assert!(min.saturating_duration_since(epoch) == Duration::ZERO);
201
    /// ```
202
10162
    fn saturating_duration_since(&self, earlier: Self) -> Duration {
203
10162
        self.checked_duration_since(earlier)
204
10162
            .unwrap_or(Duration::ZERO)
205
10162
    }
206
}
207

            
208
// Use nightly implementation if compiled with the nightly feature.
209
#[cfg(saturating_time_nightly)]
210
impl SaturatingTime for SystemTime {
211
    fn max_value() -> Self {
212
        Self::MAX
213
    }
214

            
215
    fn min_value() -> Self {
216
        Self::MIN
217
    }
218

            
219
    fn saturating_add(self, duration: Duration) -> Self {
220
        Self::saturating_add(&self, duration)
221
    }
222

            
223
    fn saturating_sub(self, duration: Duration) -> Self {
224
        Self::saturating_sub(&self, duration)
225
    }
226

            
227
    fn saturating_duration_since(&self, earlier: Self) -> Duration {
228
        Self::saturating_duration_since(&self, earlier)
229
    }
230
}
231

            
232
// Otherwise, use the default one.
233
#[cfg(not(saturating_time_nightly))]
234
impl SaturatingTime for SystemTime {}
235

            
236
impl SaturatingTime for Instant {
237
    // Override to use the provided implementation from the standard library.
238
5
    fn saturating_duration_since(&self, earlier: Self) -> Duration {
239
5
        Self::saturating_duration_since(self, earlier)
240
5
    }
241
}
242

            
243
#[cfg(test)]
244
mod tests {
245
    // @@ begin test lint list maintained by maint/add_warning @@
246
    #![allow(clippy::bool_assert_comparison)]
247
    #![allow(clippy::clone_on_copy)]
248
    #![allow(clippy::dbg_macro)]
249
    #![allow(clippy::mixed_attributes_style)]
250
    #![allow(clippy::print_stderr)]
251
    #![allow(clippy::print_stdout)]
252
    #![allow(clippy::single_char_pattern)]
253
    #![allow(clippy::unwrap_used)]
254
    #![allow(clippy::unchecked_time_subtraction)]
255
    #![allow(clippy::useless_vec)]
256
    #![allow(clippy::needless_pass_by_value)]
257
    #![allow(clippy::string_slice)] // See arti#2571
258
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
259

            
260
    use super::*;
261
    use crate::internal;
262
    use std::{
263
        fmt::Debug,
264
        ops::{Add, Sub},
265
        time::{Instant, SystemTime},
266
    };
267

            
268
    /// Verifies the maximum and minimum values of [`SaturatingTime`] equal
269
    /// their pedant in [`internal::SaturatingTime`].
270
    fn min_max<T: SaturatingTime + PartialEq + Debug>() {
271
        assert_eq!(
272
            <T as SaturatingTime>::max_value(),
273
            <T as internal::SaturatingTime>::max_value()
274
        );
275
        assert_eq!(
276
            <T as SaturatingTime>::min_value(),
277
            <T as internal::SaturatingTime>::min_value()
278
        );
279
    }
280

            
281
    /// Verifies the saturating arithmetic for [`SaturatingTime`].
282
    fn saturating_add_sub<
283
        T: SaturatingTime + PartialEq + Debug + Add<Duration, Output = T> + Sub<Duration, Output = T>,
284
    >() {
285
        let max = <T as SaturatingTime>::max_value();
286
        assert_eq!(max.saturating_add(Duration::ZERO), max);
287
        assert_eq!(max.saturating_add(Duration::new(0, 1)), max);
288
        assert_eq!(max.saturating_sub(Duration::ZERO), max);
289
        assert_eq!(
290
            max.saturating_sub(Duration::new(0, 1)),
291
            max - Duration::new(0, 1)
292
        );
293

            
294
        let min = <T as SaturatingTime>::min_value();
295
        assert_eq!(min.saturating_sub(Duration::ZERO), min);
296
        assert_eq!(min.saturating_sub(Duration::new(0, 1)), min);
297
        assert_eq!(min.saturating_add(Duration::ZERO), min);
298
        assert_eq!(
299
            min.saturating_add(Duration::new(0, 1)),
300
            min + Duration::new(0, 1)
301
        );
302
    }
303

            
304
    /// Verifies whether the saturating logic behind [`Duration`] types work.
305
    fn saturating_duration<T: SaturatingTime + PartialEq + Debug>() {
306
        // The duration from the same anchor should always be zero.
307
        let anchor = T::anchor();
308
        assert_eq!(anchor.saturating_duration_since(anchor), Duration::ZERO);
309

            
310
        // Try with a later anchor.
311
        let later_anchor = anchor.checked_add(Duration::from_secs(1)).unwrap();
312
        assert!(later_anchor.saturating_duration_since(anchor) == Duration::from_secs(1));
313
        assert_eq!(
314
            anchor.saturating_duration_since(later_anchor),
315
            Duration::ZERO
316
        );
317

            
318
        // Try with min and max.
319
        let max = <T as SaturatingTime>::max_value();
320
        let min = <T as SaturatingTime>::min_value();
321

            
322
        // This first assertion might not be so portable, maybe remove it if
323
        // this becomes a problem.
324
        assert_eq!(max.saturating_duration_since(min), Duration::MAX);
325
        assert_eq!(min.saturating_duration_since(max), Duration::ZERO);
326
    }
327

            
328
    /// Calls [`min_max()`] using [`SystemTime`].
329
    #[test]
330
    fn system_time_min_max() {
331
        min_max::<SystemTime>();
332
    }
333

            
334
    /// Calls [`min_max()`] using [`Instant`].
335
    #[test]
336
    fn instant_min_max() {
337
        min_max::<Instant>();
338
    }
339

            
340
    /// Calls [`saturating_add_sub()`] and [`saturating_duration()`] using [`SystemTime`].
341
    #[test]
342
    fn system_time_saturating() {
343
        saturating_add_sub::<SystemTime>();
344
        saturating_duration::<SystemTime>();
345
    }
346

            
347
    /// Calls [`saturating_add_sub()`] and [`saturating_duration()`] using [`Instant`].
348
    #[test]
349
    fn instant_saturating() {
350
        saturating_add_sub::<Instant>();
351
        saturating_duration::<Instant>();
352
    }
353
}