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}