use instant::{Duration, Instant};
use type_ulid::TypeUlid;
mod stopwatch;
pub use stopwatch::*;
mod timer;
pub use timer::*;
#[derive(Clone, Copy, Debug, TypeUlid)]
#[ulid = "01GNR4DNDZRH0E9XCSV79WRGXH"]
pub struct Time {
startup: Instant,
last_update: Option<Instant>,
first_update: Option<Instant>,
paused: bool,
delta: Duration,
delta_seconds: f32,
delta_seconds_f64: f64,
elapsed: Duration,
elapsed_seconds: f32,
elapsed_seconds_f64: f64,
}
impl Default for Time {
fn default() -> Self {
Self {
paused: false,
first_update: None,
last_update: None,
delta_seconds: 0.0,
delta: Duration::ZERO,
elapsed_seconds: 0.0,
delta_seconds_f64: 0.0,
startup: Instant::now(),
elapsed: Duration::ZERO,
elapsed_seconds_f64: 0.0,
}
}
}
impl Time {
pub fn new(startup: Instant) -> Self {
Self {
startup,
..Default::default()
}
}
pub fn update(&mut self) {
let now = Instant::now();
self.update_with_instant(now);
}
pub fn update_with_instant(&mut self, instant: Instant) {
let raw_delta = instant - self.last_update.unwrap_or(self.startup);
let delta = if self.paused {
Duration::ZERO
} else {
raw_delta
};
if self.last_update.is_some() {
self.delta = delta;
self.delta_seconds = self.delta.as_secs_f32();
self.delta_seconds_f64 = self.delta.as_secs_f64();
} else {
self.first_update = Some(instant);
}
self.elapsed += delta;
self.elapsed_seconds = self.elapsed.as_secs_f32();
self.elapsed_seconds_f64 = self.elapsed.as_secs_f64();
self.last_update = Some(instant);
}
pub fn advance_exact(&mut self, duration: Duration) {
let next_instant = self.last_update.unwrap_or_else(Instant::now) + duration;
self.update_with_instant(next_instant);
}
#[inline]
pub fn delta(&self) -> Duration {
self.delta
}
#[inline]
pub fn delta_seconds(&self) -> f32 {
self.delta_seconds
}
#[inline]
pub fn delta_seconds_f64(&self) -> f64 {
self.delta_seconds_f64
}
#[inline]
pub fn elapsed(&self) -> Duration {
self.elapsed
}
#[inline]
pub fn elapsed_seconds(&self) -> f32 {
self.elapsed_seconds
}
#[inline]
pub fn elapsed_seconds_f64(&self) -> f64 {
self.elapsed_seconds_f64
}
#[inline]
pub fn pause(&mut self) {
self.paused = true;
}
#[inline]
pub fn unpause(&mut self) {
self.paused = false;
}
#[inline]
pub fn is_paused(&self) -> bool {
self.paused
}
}
#[cfg(test)]
#[allow(clippy::float_cmp)]
mod tests {
use super::Time;
use std::time::{Duration, Instant};
#[test]
fn update_test() {
let start_instant = Instant::now();
let mut time = Time::new(start_instant);
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.delta_seconds(), 0.0);
assert_eq!(time.delta_seconds_f64(), 0.0);
assert_eq!(time.elapsed(), Duration::ZERO);
assert_eq!(time.elapsed_seconds(), 0.0);
assert_eq!(time.elapsed_seconds_f64(), 0.0);
let first_update_instant = Instant::now();
time.update_with_instant(first_update_instant);
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.delta_seconds(), 0.0);
assert_eq!(time.delta_seconds_f64(), 0.0);
assert_eq!(time.elapsed(), first_update_instant - start_instant,);
assert_eq!(
time.elapsed_seconds(),
(first_update_instant - start_instant).as_secs_f32(),
);
assert_eq!(
time.elapsed_seconds_f64(),
(first_update_instant - start_instant).as_secs_f64(),
);
let second_update_instant = Instant::now();
time.update_with_instant(second_update_instant);
assert_eq!(time.delta(), second_update_instant - first_update_instant);
assert_eq!(
time.delta_seconds(),
(second_update_instant - first_update_instant).as_secs_f32(),
);
assert_eq!(
time.delta_seconds_f64(),
(second_update_instant - first_update_instant).as_secs_f64(),
);
assert_eq!(time.elapsed(), second_update_instant - start_instant,);
assert_eq!(
time.elapsed_seconds(),
(second_update_instant - start_instant).as_secs_f32(),
);
assert_eq!(
time.elapsed_seconds_f64(),
(second_update_instant - start_instant).as_secs_f64(),
);
}
#[test]
fn pause_test() {
let start_instant = Instant::now();
let mut time = Time::new(start_instant);
let first_update_instant = Instant::now();
time.update_with_instant(first_update_instant);
assert!(!time.is_paused());
time.pause();
assert!(time.is_paused());
let second_update_instant = Instant::now();
time.update_with_instant(second_update_instant);
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.elapsed(), first_update_instant - start_instant);
time.unpause();
assert!(!time.is_paused());
let third_update_instant = Instant::now();
time.update_with_instant(third_update_instant);
assert_eq!(time.delta(), third_update_instant - second_update_instant);
assert_eq!(
time.elapsed(),
(third_update_instant - second_update_instant) + (first_update_instant - start_instant),
);
}
}