saturating_time/lib.rs
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#![deny(clippy::print_stderr)]
27#![deny(clippy::print_stdout)]
28#![warn(clippy::rc_buffer)]
29#![deny(clippy::ref_option_ref)]
30#![warn(clippy::semicolon_if_nothing_returned)]
31#![warn(clippy::trait_duplication_in_bounds)]
32#![deny(clippy::unchecked_time_subtraction)]
33#![deny(clippy::unnecessary_wraps)]
34#![warn(clippy::unseparated_literal_suffix)]
35#![deny(clippy::unwrap_used)]
36#![deny(clippy::mod_module_files)]
37#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
38#![allow(clippy::uninlined_format_args)]
39#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
40#![allow(clippy::result_large_err)] // temporary workaround for arti#587
41#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
42#![allow(clippy::needless_lifetimes)] // See arti#1765
43#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
44#![allow(clippy::collapsible_if)] // See arti#2342
45#![deny(clippy::unused_async)]
46#![deny(clippy::string_slice)] // See arti#2571
47//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
48#![allow(unexpected_cfgs)]
49#![forbid(unsafe_code)]
50#![cfg_attr(
51 saturating_time_nightly,
52 feature(time_systemtime_limits, time_saturating_systemtime)
53)]
54
55use std::time::{Duration, Instant, SystemTime};
56
57mod internal;
58
59/// The core trait of this crait, [`SaturatingTime`].
60///
61/// This trait provides methods for performing saturating arithmetic on those
62/// types in [`std::time`] that not already come with such a functionality,
63/// such as [`SystemTime`] or [`Instant`].
64///
65/// The trait itself is not implementable from the outside, because it is sealed
66/// by an internal trait.
67///
68/// See the methods or the top-level documentation for concrete code examples.
69pub trait SaturatingTime: internal::SaturatingTime {
70 /// Returns the maximum value for this type on the current platform.
71 ///
72 /// This limit is highly platform specific. It differs heavily between
73 /// Unix, Windows, and other operating systems.
74 ///
75 /// The limit itself is calculated dynamically during runtime with a correct
76 /// algorithm. Afterwards, it gets stored in a lazy static value, meaning
77 /// that only the first call to it will be slightly more expensive, whereas
78 /// all latter calls will result in an immediate return of the value.
79 ///
80 /// # Examples
81 ///
82 /// ```
83 /// use std::time::{Duration, SystemTime};
84 /// use saturating_time::SaturatingTime;
85 ///
86 /// let max = SystemTime::max_value();
87 ///
88 /// // Adding zero to the maximum value will change nothing.
89 /// assert!(max.checked_add(Duration::ZERO).is_some());
90 ///
91 /// // Adding 1ns to the maximum value will fail.
92 /// assert!(max.checked_add(Duration::new(0, 1)).is_none());
93 ///
94 /// // Subtracting 1ns from the maximum value will work of course.
95 /// assert!(max.checked_sub(Duration::new(0, 1)).is_some());
96 /// ```
97 fn max_value() -> Self {
98 internal::SaturatingTime::max_value()
99 }
100
101 /// Returns the minimum value for this type on the current platform.
102 ///
103 /// This limit is highly platform specific. It differs heavily between
104 /// Unix, Windows, and other operating systems.
105 ///
106 /// The limit itself is calculated dynamically during runtime with a correct
107 /// algorithm. Afterwards, it gets stored in a lazy static value, meaning
108 /// that only the first call to it will be slightly more expensive, whereas
109 /// all latter calls will result in an immediate return of the value.
110 ///
111 /// # Examples
112 ///
113 /// ```
114 /// use std::time::{Duration, SystemTime};
115 /// use saturating_time::SaturatingTime;
116 ///
117 /// let min = SystemTime::min_value();
118 ///
119 /// // Subtracting a zero from the minimum value will change nothing.
120 /// assert!(min.checked_sub(Duration::ZERO).is_some());
121 ///
122 /// // Subtracting 1ns from the minimum value will fail.
123 /// assert!(min.checked_sub(Duration::new(0, 1)).is_none());
124 ///
125 /// // Adding 1ns to the minimum value will work of course.
126 /// assert!(min.checked_add(Duration::new(0, 1)).is_some());
127 /// ```
128 fn min_value() -> Self {
129 internal::SaturatingTime::min_value()
130 }
131
132 /// Performs a saturating addition of a [`Duration`].
133 ///
134 /// The resulting value will saturate to [`SaturatingTime::max_value()`] in
135 /// the case the addition would have caused an overflow of value.
136 ///
137 /// # Examples
138 ///
139 /// ```
140 /// use std::time::{Duration, SystemTime};
141 /// use saturating_time::SaturatingTime;
142 ///
143 /// let max = SystemTime::max_value();
144 ///
145 /// // Adding zero will change nothing.
146 /// assert_eq!(max.saturating_add(Duration::ZERO), max);
147 ///
148 /// // Adding 1ns would overflow so we saturate to the maximum.
149 /// assert_eq!(max.saturating_add(Duration::new(0, 1)), max);
150 /// ```
151 fn saturating_add(self, duration: Duration) -> Self {
152 self.checked_add(duration)
153 .unwrap_or(SaturatingTime::max_value())
154 }
155
156 /// Performs a saturating subtraction of a [`Duration`].
157 ///
158 /// The resulting value will saturate to [`SaturatingTime::min_value()`] in
159 /// the case the subtraction would have caused an overflow of value.
160 ///
161 /// # Examples
162 ///
163 /// ```
164 /// use std::time::{Duration, SystemTime};
165 /// use saturating_time::SaturatingTime;
166 ///
167 /// let min = SystemTime::min_value();
168 ///
169 /// // Subtracting zero will change nothing.
170 /// assert_eq!(min.saturating_sub(Duration::ZERO), min);
171 ///
172 /// // Subtracting 1ns would overflow so we saturate to the minimum.
173 /// assert_eq!(min.saturating_sub(Duration::new(0, 1)), min);
174 /// ```
175 fn saturating_sub(self, duration: Duration) -> Self {
176 self.checked_sub(duration)
177 .unwrap_or(SaturatingTime::min_value())
178 }
179
180 /// Performs a saturating time difference calculation between two points.
181 ///
182 /// The resulting value will saturate to [`Duration::ZERO`] in the case that
183 /// the `earlier` point in time is actually not earlier, thereby resulting
184 /// in a negative difference.
185 ///
186 /// # Examples
187 ///
188 /// ```
189 /// use std::time::{Duration, SystemTime};
190 /// use saturating_time::SaturatingTime;
191 ///
192 /// let epoch = SystemTime::UNIX_EPOCH;
193 /// let now = SystemTime::now();
194 /// let min = SystemTime::min_value();
195 ///
196 /// assert!(now.saturating_duration_since(epoch).as_secs() > 0);
197 /// assert!(epoch.saturating_duration_since(epoch) == Duration::ZERO);
198 /// assert!(min.saturating_duration_since(epoch) == Duration::ZERO);
199 /// ```
200 fn saturating_duration_since(&self, earlier: Self) -> Duration {
201 self.checked_duration_since(earlier)
202 .unwrap_or(Duration::ZERO)
203 }
204}
205
206// Use nightly implementation if compiled with the nightly feature.
207#[cfg(saturating_time_nightly)]
208impl SaturatingTime for SystemTime {
209 fn max_value() -> Self {
210 Self::MAX
211 }
212
213 fn min_value() -> Self {
214 Self::MIN
215 }
216
217 fn saturating_add(self, duration: Duration) -> Self {
218 Self::saturating_add(&self, duration)
219 }
220
221 fn saturating_sub(self, duration: Duration) -> Self {
222 Self::saturating_sub(&self, duration)
223 }
224
225 fn saturating_duration_since(&self, earlier: Self) -> Duration {
226 Self::saturating_duration_since(&self, earlier)
227 }
228}
229
230// Otherwise, use the default one.
231#[cfg(not(saturating_time_nightly))]
232impl SaturatingTime for SystemTime {}
233
234impl SaturatingTime for Instant {
235 // Override to use the provided implementation from the standard library.
236 fn saturating_duration_since(&self, earlier: Self) -> Duration {
237 Self::saturating_duration_since(self, earlier)
238 }
239}
240
241#[cfg(test)]
242mod tests {
243 // @@ begin test lint list maintained by maint/add_warning @@
244 #![allow(clippy::bool_assert_comparison)]
245 #![allow(clippy::clone_on_copy)]
246 #![allow(clippy::dbg_macro)]
247 #![allow(clippy::mixed_attributes_style)]
248 #![allow(clippy::print_stderr)]
249 #![allow(clippy::print_stdout)]
250 #![allow(clippy::single_char_pattern)]
251 #![allow(clippy::unwrap_used)]
252 #![allow(clippy::unchecked_time_subtraction)]
253 #![allow(clippy::useless_vec)]
254 #![allow(clippy::needless_pass_by_value)]
255 #![allow(clippy::string_slice)] // See arti#2571
256 //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
257
258 use super::*;
259 use crate::internal;
260 use std::{
261 fmt::Debug,
262 ops::{Add, Sub},
263 time::{Instant, SystemTime},
264 };
265
266 /// Verifies the maximum and minimum values of [`SaturatingTime`] equal
267 /// their pedant in [`internal::SaturatingTime`].
268 fn min_max<T: SaturatingTime + PartialEq + Debug>() {
269 assert_eq!(
270 <T as SaturatingTime>::max_value(),
271 <T as internal::SaturatingTime>::max_value()
272 );
273 assert_eq!(
274 <T as SaturatingTime>::min_value(),
275 <T as internal::SaturatingTime>::min_value()
276 );
277 }
278
279 /// Verifies the saturating arithmetic for [`SaturatingTime`].
280 fn saturating_add_sub<
281 T: SaturatingTime + PartialEq + Debug + Add<Duration, Output = T> + Sub<Duration, Output = T>,
282 >() {
283 let max = <T as SaturatingTime>::max_value();
284 assert_eq!(max.saturating_add(Duration::ZERO), max);
285 assert_eq!(max.saturating_add(Duration::new(0, 1)), max);
286 assert_eq!(max.saturating_sub(Duration::ZERO), max);
287 assert_eq!(
288 max.saturating_sub(Duration::new(0, 1)),
289 max - Duration::new(0, 1)
290 );
291
292 let min = <T as SaturatingTime>::min_value();
293 assert_eq!(min.saturating_sub(Duration::ZERO), min);
294 assert_eq!(min.saturating_sub(Duration::new(0, 1)), min);
295 assert_eq!(min.saturating_add(Duration::ZERO), min);
296 assert_eq!(
297 min.saturating_add(Duration::new(0, 1)),
298 min + Duration::new(0, 1)
299 );
300 }
301
302 /// Verifies whether the saturating logic behind [`Duration`] types work.
303 fn saturating_duration<T: SaturatingTime + PartialEq + Debug>() {
304 // The duration from the same anchor should always be zero.
305 let anchor = T::anchor();
306 assert_eq!(anchor.saturating_duration_since(anchor), Duration::ZERO);
307
308 // Try with a later anchor.
309 let later_anchor = anchor.checked_add(Duration::from_secs(1)).unwrap();
310 assert!(later_anchor.saturating_duration_since(anchor) == Duration::from_secs(1));
311 assert_eq!(
312 anchor.saturating_duration_since(later_anchor),
313 Duration::ZERO
314 );
315
316 // Try with min and max.
317 let max = <T as SaturatingTime>::max_value();
318 let min = <T as SaturatingTime>::min_value();
319
320 // This first assertion might not be so portable, maybe remove it if
321 // this becomes a problem.
322 assert_eq!(max.saturating_duration_since(min), Duration::MAX);
323 assert_eq!(min.saturating_duration_since(max), Duration::ZERO);
324 }
325
326 /// Calls [`min_max()`] using [`SystemTime`].
327 #[test]
328 fn system_time_min_max() {
329 min_max::<SystemTime>();
330 }
331
332 /// Calls [`min_max()`] using [`Instant`].
333 #[test]
334 fn instant_min_max() {
335 min_max::<Instant>();
336 }
337
338 /// Calls [`saturating_add_sub()`] and [`saturating_duration()`] using [`SystemTime`].
339 #[test]
340 fn system_time_saturating() {
341 saturating_add_sub::<SystemTime>();
342 saturating_duration::<SystemTime>();
343 }
344
345 /// Calls [`saturating_add_sub()`] and [`saturating_duration()`] using [`Instant`].
346 #[test]
347 fn instant_saturating() {
348 saturating_add_sub::<Instant>();
349 saturating_duration::<Instant>();
350 }
351}