use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimerState {
Running,
Paused,
Finished,
}
#[derive(Debug, Clone)]
pub struct Timer {
duration: Duration,
elapsed: Duration,
repeating: bool,
state: TimerState,
just_finished: bool,
}
impl Timer {
pub fn new(duration: Duration) -> Self {
Self {
duration,
elapsed: Duration::ZERO,
repeating: false,
state: TimerState::Running,
just_finished: false,
}
}
pub fn repeating(duration: Duration) -> Self {
Self {
duration,
elapsed: Duration::ZERO,
repeating: true,
state: TimerState::Running,
just_finished: false,
}
}
pub fn from_seconds(seconds: f32) -> Self {
Self::new(Duration::from_secs_f32(seconds))
}
pub fn repeating_from_seconds(seconds: f32) -> Self {
Self::repeating(Duration::from_secs_f32(seconds))
}
pub fn from_millis(millis: u64) -> Self {
Self::new(Duration::from_millis(millis))
}
pub fn repeating_from_millis(millis: u64) -> Self {
Self::repeating(Duration::from_millis(millis))
}
pub fn tick(&mut self, delta: Duration) {
self.just_finished = false;
if self.state != TimerState::Running {
return;
}
let _old_elapsed = self.elapsed;
self.elapsed += delta;
if self.elapsed >= self.duration {
self.just_finished = true;
if self.repeating {
let overflow = self.elapsed - self.duration;
self.elapsed = overflow;
while self.elapsed >= self.duration {
self.elapsed -= self.duration;
}
} else {
self.elapsed = self.duration;
self.state = TimerState::Finished;
}
}
}
pub fn finished(&self) -> bool {
match self.state {
TimerState::Finished => true,
_ => self.repeating && self.just_finished,
}
}
pub fn just_finished(&self) -> bool {
self.just_finished
}
pub fn elapsed(&self) -> Duration {
self.elapsed
}
pub fn elapsed_seconds(&self) -> f32 {
self.elapsed.as_secs_f32()
}
pub fn duration(&self) -> Duration {
self.duration
}
pub fn duration_seconds(&self) -> f32 {
self.duration.as_secs_f32()
}
pub fn percent(&self) -> f32 {
if self.duration.is_zero() {
1.0
} else {
(self.elapsed.as_secs_f32() / self.duration.as_secs_f32()).min(1.0)
}
}
pub fn remaining(&self) -> Duration {
self.duration.saturating_sub(self.elapsed)
}
pub fn remaining_seconds(&self) -> f32 {
self.remaining().as_secs_f32()
}
pub fn reset(&mut self) {
self.elapsed = Duration::ZERO;
self.state = TimerState::Running;
self.just_finished = false;
}
pub fn pause(&mut self) {
if self.state == TimerState::Running {
self.state = TimerState::Paused;
}
}
pub fn resume(&mut self) {
if self.state == TimerState::Paused {
self.state = TimerState::Running;
}
}
pub fn set_duration(&mut self, duration: Duration) {
self.duration = duration;
if self.elapsed >= self.duration {
if self.repeating {
self.elapsed = Duration::ZERO;
self.just_finished = true;
} else {
self.elapsed = self.duration;
self.state = TimerState::Finished;
self.just_finished = true;
}
}
}
pub fn set_repeating(&mut self, repeating: bool) {
self.repeating = repeating;
if repeating {
if self.state == TimerState::Finished {
self.elapsed = Duration::ZERO;
self.state = TimerState::Running;
}
} else {
if self.elapsed >= self.duration {
self.state = TimerState::Finished;
}
}
}
pub fn is_repeating(&self) -> bool {
self.repeating
}
pub fn is_running(&self) -> bool {
self.state == TimerState::Running
}
pub fn is_paused(&self) -> bool {
self.state == TimerState::Paused
}
pub fn state(&self) -> TimerState {
self.state
}
pub fn finish(&mut self) {
self.elapsed = self.duration;
self.just_finished = true;
if !self.repeating {
self.state = TimerState::Finished;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
use approx::assert_relative_eq;
#[test]
fn test_timer_creation() {
let timer = Timer::from_seconds(2.0);
assert_eq!(timer.duration_seconds(), 2.0);
assert!(!timer.is_repeating());
assert!(timer.is_running());
assert!(!timer.finished());
}
#[test]
fn test_repeating_timer_creation() {
let timer = Timer::repeating_from_seconds(1.5);
assert_eq!(timer.duration_seconds(), 1.5);
assert!(timer.is_repeating());
assert!(timer.is_running());
}
#[test]
fn test_timer_tick() {
let mut timer = Timer::from_seconds(1.0);
timer.tick(Duration::from_millis(500));
assert_eq!(timer.percent(), 0.5);
assert!(!timer.finished());
assert!(!timer.just_finished());
timer.tick(Duration::from_millis(500));
assert!(timer.finished());
assert!(timer.just_finished());
assert_eq!(timer.percent(), 1.0);
timer.tick(Duration::from_millis(1));
assert!(timer.finished());
assert!(!timer.just_finished()); }
#[test]
fn test_repeating_timer() {
let mut timer = Timer::repeating_from_seconds(1.0);
timer.tick(Duration::from_secs(1));
assert!(timer.finished());
assert!(timer.just_finished());
timer.tick(Duration::from_millis(1));
assert!(!timer.finished());
assert!(!timer.just_finished());
assert!(timer.elapsed_seconds() < 0.1);
}
#[test]
fn test_timer_overflow() {
let mut timer = Timer::repeating_from_seconds(1.0);
timer.tick(Duration::from_millis(2500));
assert!(timer.just_finished());
assert_relative_eq!(timer.elapsed_seconds(), 0.5, epsilon = 1e-3);
}
#[test]
fn test_timer_pause_resume() {
let mut timer = Timer::from_seconds(1.0);
timer.tick(Duration::from_millis(300));
assert_eq!(timer.percent(), 0.3);
timer.pause();
assert!(timer.is_paused());
timer.tick(Duration::from_millis(500));
assert_eq!(timer.percent(), 0.3);
timer.resume();
assert!(timer.is_running());
timer.tick(Duration::from_millis(700));
assert!(timer.finished());
}
#[test]
fn test_timer_reset() {
let mut timer = Timer::from_seconds(1.0);
timer.tick(Duration::from_millis(800));
assert_eq!(timer.percent(), 0.8);
timer.reset();
assert_eq!(timer.percent(), 0.0);
assert!(!timer.finished());
assert!(timer.is_running());
}
#[test]
fn test_timer_set_duration() {
let mut timer = Timer::from_seconds(2.0);
timer.tick(Duration::from_secs(1));
timer.set_duration(Duration::from_secs(3));
assert_eq!(timer.percent(), 1.0 / 3.0);
timer.set_duration(Duration::from_millis(500));
assert!(timer.finished());
assert!(timer.just_finished());
}
#[test]
fn test_timer_remaining() {
let mut timer = Timer::from_seconds(5.0);
timer.tick(Duration::from_secs(2));
assert_eq!(timer.remaining(), Duration::from_secs(3));
assert_eq!(timer.remaining_seconds(), 3.0);
}
#[test]
fn test_timer_finish() {
let mut timer = Timer::from_seconds(10.0);
timer.finish();
assert!(timer.finished());
assert!(timer.just_finished());
assert_eq!(timer.percent(), 1.0);
}
#[test]
fn test_timer_state_transitions() {
let mut timer = Timer::from_seconds(1.0);
assert_eq!(timer.state(), TimerState::Running);
timer.pause();
assert_eq!(timer.state(), TimerState::Paused);
timer.resume();
assert_eq!(timer.state(), TimerState::Running);
timer.tick(Duration::from_secs(1));
assert_eq!(timer.state(), TimerState::Finished);
}
#[test]
fn test_zero_duration_timer() {
let mut timer = Timer::new(Duration::ZERO);
assert_eq!(timer.percent(), 1.0);
timer.tick(Duration::from_millis(1));
assert!(timer.finished());
assert!(timer.just_finished());
}
#[test]
fn test_timer_set_repeating() {
let mut timer = Timer::from_seconds(1.0);
timer.tick(Duration::from_secs(1));
assert!(timer.finished());
timer.set_repeating(true);
timer.tick(Duration::from_millis(1));
assert!(!timer.finished());
assert!(timer.elapsed_seconds() < 0.1);
}
#[test]
fn test_timer_from_millis() {
let timer = Timer::from_millis(500);
assert_eq!(timer.duration(), Duration::from_millis(500));
assert!(!timer.is_repeating());
}
#[test]
fn test_timer_repeating_from_millis() {
let timer = Timer::repeating_from_millis(250);
assert_eq!(timer.duration(), Duration::from_millis(250));
assert!(timer.is_repeating());
}
#[test]
fn test_timer_pause_prevents_finish() {
let mut timer = Timer::from_seconds(1.0);
timer.tick(Duration::from_millis(500));
timer.pause();
timer.tick(Duration::from_secs(10));
assert!(!timer.finished());
assert_relative_eq!(timer.percent(), 0.5, epsilon = 1e-3);
}
#[test]
fn test_timer_resume_finished_timer() {
let mut timer = Timer::from_seconds(1.0);
timer.tick(Duration::from_secs(1));
assert!(timer.finished());
timer.resume();
assert!(timer.finished());
}
#[test]
fn test_timer_pause_already_paused() {
let mut timer = Timer::from_seconds(1.0);
timer.pause();
timer.pause(); assert!(timer.is_paused());
}
#[test]
fn test_timer_resume_when_running() {
let mut timer = Timer::from_seconds(1.0);
timer.resume(); assert!(timer.is_running());
}
#[test]
fn test_timer_elapsed_seconds() {
let mut timer = Timer::from_seconds(2.0);
timer.tick(Duration::from_millis(750));
assert_relative_eq!(timer.elapsed_seconds(), 0.75, epsilon = 1e-3);
}
#[test]
fn test_timer_remaining_after_finish() {
let mut timer = Timer::from_seconds(1.0);
timer.tick(Duration::from_secs(2));
assert_eq!(timer.remaining(), Duration::ZERO);
}
#[test]
fn test_repeating_timer_rapid_ticks() {
let mut timer = Timer::repeating(Duration::from_millis(100));
for _ in 0..20 {
timer.tick(Duration::from_millis(10));
}
assert!(timer.elapsed_seconds() < 0.1);
}
#[test]
fn test_timer_set_duration_while_running() {
let mut timer = Timer::from_seconds(5.0);
timer.tick(Duration::from_secs(2));
timer.set_duration(Duration::from_secs(10));
assert!(!timer.finished());
assert_relative_eq!(timer.percent(), 0.2, epsilon = 1e-3);
}
#[test]
fn test_timer_finish_repeating() {
let mut timer = Timer::repeating_from_seconds(1.0);
timer.finish();
assert!(timer.just_finished());
assert_eq!(timer.state(), TimerState::Running);
}
}