libsw_core/
stopwatch.rs

1// libsw: stopwatch library
2// copyright (C) 2022-2023 Ula Shipman <ula.hello@mailbox.org>
3// licensed under MIT OR Apache-2.0
4
5// TODO: inconsistent occurs vs occurred
6
7use ::core::hash::{Hash, Hasher};
8use ::core::ops;
9use ::core::time::Duration;
10
11use crate::canonical::Canonical;
12use crate::Instant;
13
14/// A stopwatch measures and accumulates elapsed time between starts and stops.
15///
16/// Stopwatches work with any type that implements [`Instant`].
17///
18/// # Notes
19///
20/// It is possible to craft two stopwatches whose internal components differ,
21/// but are equal according to [`PartialEq`], [`Eq`], and [`Hash`].
22///
23/// ```
24/// # use libsw_core::Sw;
25/// # use core::time::Duration;
26/// # use std::time::Instant;
27/// let elapsed = Duration::from_secs(10);
28/// let start = Instant::now();
29/// let sw_1 = Sw {
30///     elapsed,
31///     start: Some(start),
32/// };
33/// let sw_2 = Sw {
34///     // `elapsed()` is 1s less
35///     elapsed: elapsed - Duration::from_secs(1),
36///     // now with start pushed back, `elapsed()` is equal
37///     start: Some(start - Duration::from_secs(1)),
38/// };
39///
40/// // different components, but they are equal!
41/// assert_eq!(sw_1, sw_2);
42/// ```
43#[derive(Clone, Copy, Debug)]
44pub struct Stopwatch<I: Instant> {
45    /// Accumulated elapsed time.
46    pub elapsed: Duration,
47    /// The instant at which the stopwatch was started, if it is running.
48    /// Otherwise, [`None`].
49    pub start: Option<I>,
50}
51
52impl<I: Instant> Stopwatch<I> {
53    /// Returns a stopped stopwatch with zero elapsed time.
54    ///
55    /// # Examples
56    ///
57    /// ```
58    /// # use libsw_core::Sw;
59    /// # use core::time::Duration;
60    /// let sw = Sw::new();
61    /// assert!(sw.is_stopped());
62    /// assert_eq!(sw.elapsed(), Duration::ZERO);
63    /// ```
64    #[must_use]
65    pub const fn new() -> Self {
66        Self::with_elapsed(Duration::ZERO)
67    }
68
69    /// Returns a running stopwatch initialized with zero elapsed time.
70    ///
71    /// # Examples
72    ///
73    /// ```
74    /// # use libsw_core::Sw;
75    /// let sw = Sw::new_started();
76    /// assert!(sw.is_running());
77    /// ```
78    #[must_use]
79    pub fn new_started() -> Self {
80        Self::with_elapsed_started(Duration::ZERO)
81    }
82
83    /// Returns a stopwatch initialized with zero elapsed time, started at the
84    /// given instant.
85    ///
86    /// # Examples
87    ///
88    /// ```
89    /// # use libsw_core::Sw;
90    /// # use core::time::Duration;
91    /// # use std::time::Instant;
92    /// let now = Instant::now();
93    /// let sw_1 = Sw::new_started_at(now);
94    /// let sw_2 = Sw::new_started_at(now);
95    /// // they've both started at the same time
96    /// assert_eq!(sw_1, sw_2);
97    /// // (and had zero elapsed time when they started)
98    /// assert_eq!(sw_1.elapsed_at(now), Duration::ZERO);
99    /// ```
100    #[must_use]
101    pub const fn new_started_at(start: I) -> Self {
102        Self::from_raw(Duration::ZERO, Some(start))
103    }
104
105    /// Returns a stopped stopwatch with the given elapsed time.
106    ///
107    /// # Examples
108    ///
109    /// ```
110    /// # use libsw_core::Sw;
111    /// # use core::time::Duration;
112    /// let sw = Sw::with_elapsed(Duration::from_secs(1));
113    /// assert!(sw.is_stopped());
114    /// assert_eq!(sw.elapsed(), Duration::from_secs(1));
115    /// ```
116    #[must_use]
117    pub const fn with_elapsed(elapsed: Duration) -> Self {
118        Self::from_raw(elapsed, None)
119    }
120
121    /// Returns a running stopwatch initialized with the given elapsed time.
122    ///
123    /// # Examples
124    ///
125    /// ```
126    /// # use libsw_core::Sw;
127    /// # use core::time::Duration;
128    /// let sw = Sw::with_elapsed_started(Duration::from_secs(1));
129    /// assert!(sw.is_running());
130    /// assert!(sw.elapsed() >= Duration::from_secs(1));
131    /// ```
132    #[must_use]
133    pub fn with_elapsed_started(elapsed: Duration) -> Self {
134        Self::from_raw(elapsed, Some(I::now()))
135    }
136
137    /// Returns a stopwatch from its raw parts.
138    ///
139    /// See the [top-level documentation](`Stopwatch`) for more details.
140    #[must_use]
141    pub const fn from_raw(elapsed: Duration, start: Option<I>) -> Self {
142        Self { elapsed, start }
143    }
144
145    /// Returns `true` if the stopwatch is running.
146    ///
147    /// # Examples
148    ///
149    /// ```
150    /// # use libsw_core::Sw;
151    /// let sw = Sw::new_started();
152    /// assert!(sw.is_running());
153    /// ```
154    #[must_use]
155    pub const fn is_running(&self) -> bool {
156        self.start.is_some()
157    }
158
159    /// Returns `true` if the stopwatch is stopped.
160    ///
161    /// # Examples
162    ///
163    /// ```
164    /// # use libsw_core::Sw;
165    /// let sw = Sw::new();
166    /// assert!(sw.is_stopped());
167    /// ```
168    #[must_use]
169    pub const fn is_stopped(&self) -> bool {
170        !self.is_running()
171    }
172
173    /// Returns the total time elapsed. If overflow occurs, the elapsed time is
174    /// saturated to [`Duration::MAX`].
175    ///
176    /// # Examples
177    ///
178    /// ```
179    /// # use libsw_core::Sw;
180    /// # use core::time::Duration;
181    /// # use std::thread;
182    /// let sw = Sw::new_started();
183    /// thread::sleep(Duration::from_millis(100));
184    /// assert!(sw.elapsed() >= Duration::from_millis(100));
185    /// ```
186    #[must_use]
187    pub fn elapsed(&self) -> Duration {
188        self.elapsed_at(I::now())
189    }
190
191    /// Returns the total time elapsed, measured as if the current time were
192    /// `anchor`. If overflow occurs, the elapsed time is saturated to
193    /// [`Duration::MAX`].
194    ///
195    /// # Notes
196    ///
197    /// `anchor` saturates to the last instant the stopwatch was started.
198    ///
199    /// # Examples
200    ///
201    /// ```
202    /// # use libsw_core::Sw;
203    /// # use std::time::Instant;
204    /// let sw_1 = Sw::new_started();
205    /// let sw_2 = sw_1;
206    /// let anchor = Instant::now();
207    /// assert!(sw_1.elapsed_at(anchor) == sw_2.elapsed_at(anchor));
208    /// ```
209    #[must_use]
210    pub fn elapsed_at(&self, anchor: I) -> Duration {
211        self.checked_elapsed_at(anchor).unwrap_or(Duration::MAX)
212    }
213
214    /// Computes the total time elapsed. If overflow occurred, returns [`None`].
215    ///
216    /// # Examples
217    ///
218    /// ```
219    /// # use libsw_core::Sw;
220    /// # use core::time::Duration;
221    /// # use std::thread;
222    /// let mut sw = Sw::new_started();
223    /// thread::sleep(Duration::from_millis(100));
224    /// assert!(sw.checked_elapsed().unwrap() >= Duration::from_millis(100));
225    /// sw += Duration::MAX;
226    /// assert!(sw.checked_elapsed().is_none());
227    /// ```
228    #[must_use]
229    pub fn checked_elapsed(&self) -> Option<Duration> {
230        self.checked_elapsed_at(I::now())
231    }
232
233    /// Computes the total time elapsed, measured as if the current time were
234    /// `anchor`. If overflow occurred, returns [`None`].
235    ///
236    /// # Notes
237    ///
238    /// `anchor` saturates to the last instant the stopwatch was started.
239    #[must_use]
240    pub fn checked_elapsed_at(&self, anchor: I) -> Option<Duration> {
241        let before_start = self.elapsed;
242        if let Some(start) = self.start {
243            let after_start = anchor.saturating_duration_since(start);
244            before_start.checked_add(after_start)
245        } else {
246            Some(before_start)
247        }
248    }
249
250    /// Starts measuring the time elapsed.
251    ///
252    /// # Examples
253    ///
254    /// ```
255    /// # use libsw_core::Sw;
256    /// # use core::time::Duration;
257    /// # use std::thread;
258    /// let mut sw = Sw::new();
259    /// sw.start();
260    ///
261    /// let then = sw.elapsed();
262    /// thread::sleep(Duration::from_millis(100));
263    /// let now = sw.elapsed();
264    /// assert!(then != now);
265    /// ```
266    pub fn start(&mut self) {
267        self.start_at(I::now());
268    }
269
270    /// Starts measuring the time elapsed as if the current time were `anchor`.
271    /// If the stopwatch is already running, the prior start time is overwritten.
272    ///
273    /// # Notes
274    ///
275    /// If `anchor` is ahead of the present, [`elapsed`](Self::elapsed) will
276    /// return [`Duration::ZERO`] until the current time catches up to it.
277    ///
278    /// # Examples
279    ///
280    /// ```
281    /// # use libsw_core::Sw;
282    /// # use core::time::Duration;
283    /// # use std::thread;
284    /// # use std::time::Instant;
285    /// let mut sw_1 = Sw::new();
286    /// let mut sw_2 = Sw::new();
287    ///
288    /// let start = Instant::now();
289    /// // off to the races! at the same time!
290    /// sw_1.start_at(start);
291    /// sw_2.start_at(start);
292    ///
293    /// thread::sleep(Duration::from_millis(100));
294    /// let anchor = Instant::now();
295    ///
296    /// assert_eq!(sw_1.elapsed_at(anchor), sw_2.elapsed_at(anchor)); // 'twas a tie
297    /// assert!(sw_1.elapsed_at(anchor) >= Duration::from_millis(100));
298    /// ```
299    pub fn start_at(&mut self, anchor: I) {
300        self.start = Some(anchor);
301    }
302
303    /// Stops measuring the time elapsed since the last start.
304    ///
305    /// # Notes
306    ///
307    /// Overflows of the new elapsed time are saturated to [`Duration::MAX`].
308    /// Use [`Stopwatch::checked_stop`] to explicitly check for overflow.
309    ///
310    /// # Examples
311    ///
312    /// ```
313    /// # use libsw_core::Sw;
314    /// # use core::time::Duration;
315    /// # use std::thread;
316    /// let mut sw = Sw::new_started();
317    /// sw.stop();
318    ///
319    /// let then = sw.elapsed();
320    /// thread::sleep(Duration::from_millis(100));
321    /// let now = sw.elapsed();
322    /// assert!(then == now);
323    /// ```
324    pub fn stop(&mut self) {
325        self.stop_at(I::now());
326    }
327
328    /// Stops measuring the time elapsed since the last start as if the current
329    /// time were `anchor`.
330    ///
331    /// # Notes
332    ///
333    /// - If `anchor` is earlier than the last start, there is no effect on the
334    ///   elapsed time.
335    ///
336    /// - Overflows of the new elapsed time are saturated to [`Duration::MAX`].
337    ///   Use [`Stopwatch::checked_stop_at`] to explicitly check for overflow.
338    ///
339    /// # Examples
340    ///
341    /// ```
342    /// # use libsw_core::Sw;
343    /// # use core::time::Duration;
344    /// # use std::thread;
345    /// # use std::time::Instant;
346    /// let mut sw_1 = Sw::new_started();
347    /// let mut sw_2 = sw_1;
348    /// let stop = Instant::now();
349    /// sw_1.stop_at(stop);
350    /// sw_2.stop_at(stop);
351    /// assert_eq!(sw_1, sw_2);
352    /// ```
353    pub fn stop_at(&mut self, anchor: I) {
354        if let Some(start) = self.start.take() {
355            let after_start = anchor.saturating_duration_since(start);
356            *self = self.saturating_add(after_start);
357        }
358    }
359
360    /// Tries to stop the stopwatch. If the new elapsed time overflows, returns
361    /// `false` without mutating the stopwatch.
362    ///
363    /// # Examples
364    ///
365    /// ```
366    /// # use libsw_core::Sw;
367    /// # use core::time::Duration;
368    /// let mut sw = Sw::new_started();
369    /// assert!(sw.checked_stop());
370    /// sw.set(Duration::MAX);
371    /// sw.start();
372    /// assert!(!sw.checked_stop());
373    /// ```
374    #[must_use]
375    pub fn checked_stop(&mut self) -> bool {
376        self.checked_stop_at(I::now())
377    }
378
379    /// Tries to stop the stopwatch, as if the current time were `anchor`. If
380    /// the new elapsed time overflows, returns `false` without mutating the
381    /// stopwatch.
382    ///
383    /// # Notes
384    ///
385    /// If `anchor` is earlier than the last start, there is no effect on the
386    /// elapsed time.
387    #[must_use]
388    pub fn checked_stop_at(&mut self, anchor: I) -> bool {
389        if let Some(start) = self.start {
390            let after_start = anchor.saturating_duration_since(start);
391            if let Some(new) = self.checked_add(after_start) {
392                self.set(new.elapsed);
393            } else {
394                return false;
395            }
396        }
397        true
398    }
399
400    /// Toggles whether the stopwatch is running or stopped.
401    ///
402    /// # Notes
403    ///
404    /// See [`stop`](Self::stop) for details about how overflow is handled.
405    ///
406    /// # Examples
407    ///
408    /// ```
409    /// # use libsw_core::Sw;
410    /// let mut sw = Sw::new();
411    /// sw.toggle();
412    /// assert!(sw.is_running());
413    /// sw.toggle();
414    /// assert!(sw.is_stopped());
415    /// ```
416    pub fn toggle(&mut self) {
417        self.toggle_at(I::now());
418    }
419
420    /// Toggles whether the stopwatch is running or stopped, as if the current
421    /// time were `anchor`.
422    ///
423    /// # Notes
424    ///
425    /// See [`start_at`](Self::start_at) and [`stop_at`](Self::stop_at) for
426    /// notes about the chronology of `anchor`, as well as what happens if
427    /// overflow occurs.
428    ///
429    /// # Examples
430    ///
431    /// ```
432    /// # use libsw_core::Sw;
433    /// # use std::time::Instant;
434    /// let mut left = Sw::new();
435    /// let mut right = Sw::new_started();
436    ///
437    /// // perfect swap of left and right running
438    /// let now = Instant::now();
439    /// left.toggle_at(now);
440    /// right.toggle_at(now);
441    ///
442    /// assert!(left.is_running());
443    /// assert!(right.is_stopped());
444    /// ```
445    pub fn toggle_at(&mut self, anchor: I) {
446        if self.is_running() {
447            self.stop_at(anchor);
448        } else {
449            self.start_at(anchor);
450        }
451    }
452
453    /// Tries to toggle whether the stopwatch is running or stopped. If the new
454    /// elapsed time overflows, returns `false` without mutating the stopwatch.
455    ///
456    /// # Examples
457    ///
458    /// ```
459    /// # use libsw_core::Sw;
460    /// # use core::time::Duration;
461    /// # use std::thread;
462    /// let mut sw = Sw::with_elapsed_started(Duration::MAX);
463    /// thread::sleep(Duration::from_millis(100));
464    /// // whoops, new elapsed time can't be Duration::MAX + 100ms
465    /// assert!(!sw.checked_toggle());
466    /// ```
467    #[must_use]
468    pub fn checked_toggle(&mut self) -> bool {
469        self.checked_toggle_at(I::now())
470    }
471
472    /// Tries to toggle whether the stopwatch is running or stopped, as if the
473    /// current time were `anchor`. If the new elapsed time overflows, returns
474    /// `false` without mutating the stopwatch.
475    #[must_use]
476    pub fn checked_toggle_at(&mut self, anchor: I) -> bool {
477        if self.is_running() {
478            self.checked_stop_at(anchor)
479        } else {
480            self.start_at(anchor);
481            true
482        }
483    }
484
485    /// Stops and resets the elapsed time to zero.
486    ///
487    /// # Examples
488    ///
489    /// ```
490    /// # use libsw_core::Sw;
491    /// # use core::time::Duration;
492    /// let mut sw = Sw::with_elapsed_started(Duration::from_secs(1));
493    /// sw.reset();
494    /// assert_eq!(sw, Sw::new());
495    /// ```
496    pub fn reset(&mut self) {
497        *self = Self::new();
498    }
499
500    /// Resets the elapsed time to zero without affecting whether the stopwatch
501    /// is running.
502    ///
503    /// # Examples
504    ///
505    /// ```
506    /// # use libsw_core::Sw;
507    /// # use core::time::Duration;
508    /// let mut sw = Sw::with_elapsed_started(Duration::from_secs(1));
509    /// sw.reset_in_place();
510    /// assert!(sw.is_running());
511    /// // new elapsed time is close to zero
512    /// assert!(sw.elapsed() < Duration::from_millis(1));
513    ///
514    /// sw.stop();
515    /// sw.reset_in_place();
516    /// assert_eq!(sw, Sw::new());
517    /// ```
518    pub fn reset_in_place(&mut self) {
519        self.reset_in_place_at(Instant::now());
520    }
521
522    /// Resets the elapsed time to zero without affecting whether the stopwatch
523    /// is running.
524    ///
525    /// # Notes
526    ///
527    /// See [`start_at`](Self::start_at) for notes about the chronology of
528    /// `anchor`.
529    pub fn reset_in_place_at(&mut self, start: I) {
530        self.set_in_place_at(Duration::ZERO, start);
531    }
532
533    /// Stops and sets the total elapsed time to `new`.
534    ///
535    /// # Examples
536    ///
537    /// ```
538    /// # use libsw_core::Sw;
539    /// # use core::time::Duration;
540    /// let mut sw = Sw::new();
541    /// sw.set(Duration::from_secs(1));
542    /// assert_eq!(sw.elapsed(), Duration::from_secs(1));
543    /// ```
544    pub fn set(&mut self, new: Duration) {
545        *self = Self::with_elapsed(new);
546    }
547
548    /// Sets the total elapsed time to `new` without affecting whether the
549    /// stopwatch is running.
550    ///
551    /// # Examples
552    ///
553    /// ```
554    /// # use libsw_core::Sw;
555    /// # use core::time::Duration;
556    /// let mut sw = Sw::new();
557    /// sw.set_in_place(Duration::from_secs(1));
558    /// assert_eq!(sw.elapsed(), Duration::from_secs(1));
559    /// assert!(sw.is_stopped());
560    ///
561    /// sw.start();
562    /// sw.set_in_place(Duration::from_secs(2));
563    /// assert!(sw.elapsed() >= Duration::from_secs(2));
564    /// assert!(sw.is_running());
565    /// ```
566    pub fn set_in_place(&mut self, new: Duration) {
567        self.set_in_place_at(new, Instant::now());
568    }
569
570    /// Sets the total elapsed time to `new` as if the current time were
571    /// `anchor`, and without affecting whether the stopwatch is running.
572    ///
573    /// # Notes
574    ///
575    /// See [`start_at`](Self::start_at) for notes about the chronology of
576    /// `anchor`.
577    pub fn set_in_place_at(&mut self, new: Duration, anchor: I) {
578        let was_running = self.is_running();
579        self.set(new);
580        if was_running {
581            self.start_at(anchor);
582        }
583    }
584
585    /// Stops and sets the total elapsed time to `new`, returning the previous
586    /// elapsed time.
587    ///
588    /// # Examples
589    ///
590    /// ```
591    /// # use libsw_core::Sw;
592    /// # use core::time::Duration;
593    /// let mut sw = Sw::with_elapsed(Duration::from_secs(3));
594    /// let previous = sw.replace(Duration::from_secs(1));
595    /// assert_eq!(previous, Duration::from_secs(3));
596    /// assert_eq!(sw.elapsed(), Duration::from_secs(1));
597    /// ```
598    pub fn replace(&mut self, new: Duration) -> Duration {
599        self.replace_at(new, Instant::now())
600    }
601
602    /// Stops and sets the total elapsed time to `new`, returning the previous
603    /// elapsed time as if the current time were `anchor`.
604    ///
605    /// # Notes
606    ///
607    /// See [`elapsed_at`](Self::elapsed_at) for notes about the chronology of
608    /// `anchor`.
609    pub fn replace_at(&mut self, new: Duration, anchor: I) -> Duration {
610        let old = self.elapsed_at(anchor);
611        self.set(new);
612        old
613    }
614
615    /// Adds `dur` to the total elapsed time. If overflow occurred, the total
616    /// elapsed time is set to [`Duration::MAX`].
617    ///
618    /// ```
619    /// # use libsw_core::Sw;
620    /// # use core::time::Duration;
621    /// let mut sw = Sw::with_elapsed(Duration::from_secs(1));
622    /// sw = sw.saturating_add(Duration::from_secs(1));
623    /// assert_eq!(sw.elapsed(), Duration::from_secs(2));
624    /// sw = sw.saturating_add(Duration::MAX);
625    /// assert_eq!(sw.elapsed(), Duration::MAX);
626    /// ```
627    #[must_use]
628    pub const fn saturating_add(mut self, dur: Duration) -> Self {
629        self.elapsed = self.elapsed.saturating_add(dur);
630        self
631    }
632
633    /// Subtracts `dur` from the total elapsed time. If underflow occurred, the
634    /// total elapsed time is set to [`Duration::ZERO`].
635    ///
636    /// # Notes
637    ///
638    /// See the documentation for [`saturating_sub_at`](Self::saturating_sub_at)
639    /// for notes about positive overflow.
640    ///
641    /// # Examples
642    ///
643    /// ```
644    /// # use libsw_core::Sw;
645    /// # use core::time::Duration;
646    /// let mut sw = Sw::with_elapsed(Duration::from_secs(1));
647    /// sw = sw.saturating_sub(Duration::from_secs(1));
648    /// assert_eq!(sw.elapsed(), Duration::ZERO);
649    /// sw = sw.saturating_sub(Duration::from_secs(1));
650    /// assert_eq!(sw.elapsed(), Duration::ZERO);
651    /// ```
652    #[must_use]
653    pub fn saturating_sub(self, dur: Duration) -> Self {
654        self.saturating_sub_at(dur, I::now())
655    }
656
657    /// Subtracts `dur` from the total elapsed time, as if the current time were
658    /// `anchor`. If underflow occurred, the total elapsed time is set to
659    /// [`Duration::ZERO`].
660    ///
661    /// # Notes
662    ///
663    /// - If the elapsed time is overflowing (as in, exceeds [`Duration::MAX`]
664    ///   prior to subtraction), the elapsed time is clamped to
665    ///   [`Duration::MAX`] and *then* `dur` is subtracted from that.
666    ///
667    /// - `anchor` saturates to the last instant the stopwatch was started.
668    ///
669    /// # Examples
670    ///
671    /// ```
672    /// # use libsw_core::Sw;
673    /// # use core::time::Duration;
674    /// # use std::time::Instant;
675    /// # use std::thread;
676    /// let mut sw = Sw::new_started();
677    /// thread::sleep(Duration::from_millis(100));
678    /// let now = Instant::now();
679    /// sw = sw.saturating_sub_at(Duration::from_secs(1), now);
680    /// assert_eq!(sw.elapsed_at(now), Duration::ZERO);
681    /// ```
682    #[must_use]
683    pub fn saturating_sub_at(mut self, dur: Duration, mut anchor: I) -> Self {
684        self.saturate_anchor_to_start(&mut anchor);
685        self.saturating_sync_elapsed_at(anchor);
686        self.elapsed = self.elapsed.saturating_sub(dur);
687        self
688    }
689
690    /// Adds `dur` to the total elapsed time. If overflow occurred, returns
691    /// [`None`].
692    ///
693    /// # Examples
694    ///
695    /// ```
696    /// # use libsw_core::Sw;
697    /// # use core::time::Duration;
698    /// let mut sw = Sw::new();
699    /// sw = sw.checked_add(Duration::from_secs(1)).unwrap();
700    /// assert_eq!(sw.elapsed(), Duration::from_secs(1));
701    /// assert_eq!(sw.checked_add(Duration::MAX), None);
702    /// ```
703    #[must_use]
704    pub const fn checked_add(mut self, dur: Duration) -> Option<Self> {
705        match self.elapsed.checked_add(dur) {
706            Some(new) => {
707                self.elapsed = new;
708                Some(self)
709            }
710            None => None,
711        }
712    }
713
714    /// Subtracts `dur` from the total elapsed time. If overflow occurred,
715    /// returns [`None`].
716    ///
717    /// # Notes
718    ///
719    /// See the documentation for [`checked_sub_at`](Self::checked_sub_at) for
720    /// notes about positive overflow.
721    ///
722    /// # Examples
723    ///
724    /// ```
725    /// # use libsw_core::Sw;
726    /// # use core::time::Duration;
727    /// let mut sw = Sw::new();
728    /// assert_eq!(sw.checked_sub(Duration::from_secs(1)), None);
729    /// sw += Duration::from_secs(1);
730    /// assert_eq!(
731    ///     sw.checked_sub(Duration::from_secs(1)),
732    ///     Some(Sw::with_elapsed(Duration::ZERO)),
733    /// );
734    /// ```
735    #[must_use]
736    pub fn checked_sub(self, dur: Duration) -> Option<Self> {
737        self.checked_sub_at(dur, I::now())
738    }
739
740    /// Subtracts `dur` from the total elapsed time, as if the current time were
741    /// `anchor`. If overflow occurred, returns [`None`].
742    ///
743    /// # Notes
744    ///
745    /// - Overflow occurs if the elapsed time overflows prior to subtraction.
746    ///
747    /// - `anchor` saturates to the last instant the stopwatch was started.
748    ///
749    /// # Examples
750    ///
751    /// ```
752    /// # use libsw_core::Sw;
753    /// # use core::time::Duration;
754    /// # use std::time::Instant;
755    /// # use std::thread;
756    /// let mut sw = Sw::new_started();
757    /// thread::sleep(Duration::from_millis(100));
758    /// let now = Instant::now();
759    /// // underflow yields `None`
760    /// assert_eq!(sw.checked_sub_at(Duration::from_secs(1), now), None);
761    ///
762    /// // positive overflow yields `None`
763    /// sw.set_in_place(Duration::MAX);
764    /// assert_eq!(sw.checked_sub(Duration::ZERO), None);
765    /// assert_eq!(sw.checked_sub(Duration::from_secs(2)), None);
766    /// ```
767    #[must_use]
768    pub fn checked_sub_at(mut self, dur: Duration, mut anchor: I) -> Option<Self> {
769        self.saturate_anchor_to_start(&mut anchor);
770        self.checked_sync_elapsed_at(anchor)?;
771        let new = self.elapsed.checked_sub(dur)?;
772        self.elapsed = new;
773        Some(self)
774    }
775}
776
777// private methods
778impl<I: Instant> Stopwatch<I> {
779    /// Clamp `anchor` such that when `start` is present, `start <= anchor`.
780    fn saturate_anchor_to_start(&self, anchor: &mut I) {
781        if let Some(start) = self.start {
782            // Instant doesn't implement PartialOrd, so we measure their
783            // difference in both directions to order them.
784            // - iff `anchor` < `start`, then `past` is nonzero and `future` is zero
785            // - iff `start` < `anchor`, then `future` is nonzero and `past` is zero
786            // - iff `start` == `anchor`, then both `future` and `past` are zero
787
788            let future = anchor.saturating_duration_since(start);
789            let past = start.saturating_duration_since(*anchor);
790
791            if future < past {
792                *anchor = start;
793            }
794        }
795    }
796
797    /// Syncs changes in the elapsed time, effectively toggling the stopwatch
798    /// twice. If the new elapsed time overflows, it is saturated to
799    /// [`Duration::MAX`].
800    fn saturating_sync_elapsed_at(&mut self, anchor: I) {
801        if let Some(start) = self.start {
802            *self = self.saturating_add(anchor.saturating_duration_since(start));
803            self.start = Some(anchor);
804        }
805    }
806
807    /// Syncs changes in the elapsed time, effectively toggling the stopwatch
808    /// twice. If the new elapsed time overflows, returns [`None`] without
809    /// mutating the stopwatch.
810    #[must_use]
811    fn checked_sync_elapsed_at(&mut self, anchor: I) -> Option<()> {
812        if let Some(start) = self.start {
813            let after_start = anchor.saturating_duration_since(start);
814            *self = self.checked_add(after_start)?;
815            self.start = Some(anchor);
816        }
817        Some(())
818    }
819}
820
821impl<I: Instant> Default for Stopwatch<I> {
822    /// Returns the default stopwatch. Same as calling [`Stopwatch::new`].
823    fn default() -> Self {
824        Self::new()
825    }
826}
827
828impl<I: Instant> ops::Add<Duration> for Stopwatch<I> {
829    type Output = Self;
830
831    /// Add `dur` to `self`.
832    ///
833    /// Currently this is an alias to [`Stopwatch::checked_add`], but that
834    /// is not a stable guarentee. If you need a guarentee on the
835    /// implementation, use the [checked](Self::checked_add) or
836    /// [saturating](Self::checked_add) methods explicitly.
837    ///
838    /// # Panics
839    ///
840    /// Panics if overflow occurs.
841    #[track_caller]
842    fn add(self, dur: Duration) -> Self::Output {
843        self.checked_add(dur)
844            .expect("attempt to add stopwatch with overflow")
845    }
846}
847
848impl<I: Instant> ops::Sub<Duration> for Stopwatch<I> {
849    type Output = Self;
850
851    /// Subtract `dur` from `self`.
852    ///
853    /// Currently this is an alias to [`Stopwatch::checked_sub`], but that
854    /// is not a stable guarentee. If you need a guarentee on the
855    /// implementation, use the [checked](Self::checked_sub) or
856    /// [saturating](Self::checked_sub) methods explicitly.
857    ///
858    /// # Panics
859    ///
860    /// Panics if overflow occurs.
861    #[track_caller]
862    fn sub(self, dur: Duration) -> Self::Output {
863        self.checked_sub(dur)
864            .expect("attempt to subtract stopwatch with overflow")
865    }
866}
867
868impl<I: Instant> ops::AddAssign<Duration> for Stopwatch<I> {
869    #[track_caller]
870    fn add_assign(&mut self, dur: Duration) {
871        *self = *self + dur;
872    }
873}
874
875impl<I: Instant> ops::SubAssign<Duration> for Stopwatch<I> {
876    #[track_caller]
877    fn sub_assign(&mut self, dur: Duration) {
878        *self = *self - dur;
879    }
880}
881
882impl<I: Instant> PartialEq for Stopwatch<I> {
883    /// Tests for equality between `self` and `rhs`.
884    ///
885    /// Stopwatches are equal if whether they are running and their elapsed time
886    /// are equal.
887    fn eq(&self, rhs: &Self) -> bool {
888        Canonical::new(*self) == Canonical::new(*rhs)
889    }
890}
891
892impl<I: Instant> Eq for Stopwatch<I> {}
893
894impl<I: Instant + Hash> Hash for Stopwatch<I> {
895    /// Hashes `self` and `rhs`. These hashes are not dependent on the time of
896    /// measurement, so they can be used to test equality.
897    ///
898    /// # Support
899    ///
900    /// `I` (the [`Instant`] type used by the stopwatch) must implement
901    /// [`Hash`].
902    fn hash<H: Hasher>(&self, state: &mut H) {
903        Canonical::new(*self).hash(state);
904    }
905}