use ::core::hash::{Hash, Hasher};
use ::core::ops;
use ::core::time::Duration;
use crate::canonical::Canonical;
use crate::Instant;
#[derive(Clone, Copy, Debug)]
pub struct Stopwatch<I: Instant> {
pub elapsed: Duration,
pub start: Option<I>,
}
impl<I: Instant> Stopwatch<I> {
#[must_use]
pub const fn new() -> Self {
Self::with_elapsed(Duration::ZERO)
}
#[must_use]
pub fn new_started() -> Self {
Self::with_elapsed_started(Duration::ZERO)
}
#[must_use]
pub const fn new_started_at(start: I) -> Self {
Self::from_raw(Duration::ZERO, Some(start))
}
#[must_use]
pub const fn with_elapsed(elapsed: Duration) -> Self {
Self::from_raw(elapsed, None)
}
#[must_use]
pub fn with_elapsed_started(elapsed: Duration) -> Self {
Self::from_raw(elapsed, Some(I::now()))
}
#[must_use]
pub const fn from_raw(elapsed: Duration, start: Option<I>) -> Self {
Self { elapsed, start }
}
#[must_use]
pub const fn is_running(&self) -> bool {
self.start.is_some()
}
#[must_use]
pub const fn is_stopped(&self) -> bool {
!self.is_running()
}
#[must_use]
pub fn elapsed(&self) -> Duration {
self.elapsed_at(I::now())
}
#[must_use]
pub fn elapsed_at(&self, anchor: I) -> Duration {
self.checked_elapsed_at(anchor).unwrap_or(Duration::MAX)
}
#[must_use]
pub fn checked_elapsed(&self) -> Option<Duration> {
self.checked_elapsed_at(I::now())
}
#[must_use]
pub fn checked_elapsed_at(&self, anchor: I) -> Option<Duration> {
let before_start = self.elapsed;
if let Some(start) = self.start {
let after_start = anchor.saturating_duration_since(start);
before_start.checked_add(after_start)
} else {
Some(before_start)
}
}
pub fn start(&mut self) {
self.start_at(I::now());
}
pub fn start_at(&mut self, anchor: I) {
self.start = Some(anchor);
}
pub fn stop(&mut self) {
self.stop_at(I::now());
}
pub fn stop_at(&mut self, anchor: I) {
if let Some(start) = self.start.take() {
let after_start = anchor.saturating_duration_since(start);
*self = self.saturating_add(after_start);
}
}
#[must_use]
pub fn checked_stop(&mut self) -> bool {
self.checked_stop_at(I::now())
}
#[must_use]
pub fn checked_stop_at(&mut self, anchor: I) -> bool {
if let Some(start) = self.start {
let after_start = anchor.saturating_duration_since(start);
if let Some(new) = self.checked_add(after_start) {
self.set(new.elapsed);
} else {
return false;
}
}
true
}
pub fn toggle(&mut self) {
self.toggle_at(I::now());
}
pub fn toggle_at(&mut self, anchor: I) {
if self.is_running() {
self.stop_at(anchor);
} else {
self.start_at(anchor);
}
}
#[must_use]
pub fn checked_toggle(&mut self) -> bool {
self.checked_toggle_at(I::now())
}
#[must_use]
pub fn checked_toggle_at(&mut self, anchor: I) -> bool {
if self.is_running() {
self.checked_stop_at(anchor)
} else {
self.start_at(anchor);
true
}
}
pub fn reset(&mut self) {
*self = Self::new();
}
pub fn reset_in_place(&mut self) {
self.reset_in_place_at(Instant::now());
}
pub fn reset_in_place_at(&mut self, start: I) {
self.set_in_place_at(Duration::ZERO, start);
}
pub fn set(&mut self, new: Duration) {
*self = Self::with_elapsed(new);
}
pub fn set_in_place(&mut self, new: Duration) {
self.set_in_place_at(new, Instant::now());
}
pub fn set_in_place_at(&mut self, new: Duration, anchor: I) {
let was_running = self.is_running();
self.set(new);
if was_running {
self.start_at(anchor);
}
}
pub fn replace(&mut self, new: Duration) -> Duration {
self.replace_at(new, Instant::now())
}
pub fn replace_at(&mut self, new: Duration, anchor: I) -> Duration {
let old = self.elapsed_at(anchor);
self.set(new);
old
}
#[must_use]
pub const fn saturating_add(mut self, dur: Duration) -> Self {
self.elapsed = self.elapsed.saturating_add(dur);
self
}
#[must_use]
pub fn saturating_sub(self, dur: Duration) -> Self {
self.saturating_sub_at(dur, I::now())
}
#[must_use]
pub fn saturating_sub_at(mut self, dur: Duration, mut anchor: I) -> Self {
self.saturate_anchor_to_start(&mut anchor);
self.saturating_sync_elapsed_at(anchor);
self.elapsed = self.elapsed.saturating_sub(dur);
self
}
#[must_use]
pub const fn checked_add(mut self, dur: Duration) -> Option<Self> {
match self.elapsed.checked_add(dur) {
Some(new) => {
self.elapsed = new;
Some(self)
}
None => None,
}
}
#[must_use]
pub fn checked_sub(self, dur: Duration) -> Option<Self> {
self.checked_sub_at(dur, I::now())
}
#[must_use]
pub fn checked_sub_at(mut self, dur: Duration, mut anchor: I) -> Option<Self> {
self.saturate_anchor_to_start(&mut anchor);
self.checked_sync_elapsed_at(anchor)?;
let new = self.elapsed.checked_sub(dur)?;
self.elapsed = new;
Some(self)
}
}
impl<I: Instant> Stopwatch<I> {
fn saturate_anchor_to_start(&self, anchor: &mut I) {
if let Some(start) = self.start {
let future = anchor.saturating_duration_since(start);
let past = start.saturating_duration_since(*anchor);
if future < past {
*anchor = start;
}
}
}
fn saturating_sync_elapsed_at(&mut self, anchor: I) {
if let Some(start) = self.start {
*self = self.saturating_add(anchor.saturating_duration_since(start));
self.start = Some(anchor);
}
}
#[must_use]
fn checked_sync_elapsed_at(&mut self, anchor: I) -> Option<()> {
if let Some(start) = self.start {
let after_start = anchor.saturating_duration_since(start);
*self = self.checked_add(after_start)?;
self.start = Some(anchor);
}
Some(())
}
}
impl<I: Instant> Default for Stopwatch<I> {
fn default() -> Self {
Self::new()
}
}
impl<I: Instant> ops::Add<Duration> for Stopwatch<I> {
type Output = Self;
#[track_caller]
fn add(self, dur: Duration) -> Self::Output {
self.checked_add(dur)
.expect("attempt to add stopwatch with overflow")
}
}
impl<I: Instant> ops::Sub<Duration> for Stopwatch<I> {
type Output = Self;
#[track_caller]
fn sub(self, dur: Duration) -> Self::Output {
self.checked_sub(dur)
.expect("attempt to subtract stopwatch with overflow")
}
}
impl<I: Instant> ops::AddAssign<Duration> for Stopwatch<I> {
#[track_caller]
fn add_assign(&mut self, dur: Duration) {
*self = *self + dur;
}
}
impl<I: Instant> ops::SubAssign<Duration> for Stopwatch<I> {
#[track_caller]
fn sub_assign(&mut self, dur: Duration) {
*self = *self - dur;
}
}
impl<I: Instant> PartialEq for Stopwatch<I> {
fn eq(&self, rhs: &Self) -> bool {
Canonical::new(*self) == Canonical::new(*rhs)
}
}
impl<I: Instant> Eq for Stopwatch<I> {}
impl<I: Instant + Hash> Hash for Stopwatch<I> {
fn hash<H: Hasher>(&self, state: &mut H) {
Canonical::new(*self).hash(state);
}
}