2026-07-22 19:32:28 -05:00
|
|
|
const SECONDS_PER_MIN: i64 = 60;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
|
enum TimerState {
|
|
|
|
|
Work,
|
|
|
|
|
Break,
|
|
|
|
|
Paused,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Termato {
|
|
|
|
|
work_mins: i64,
|
|
|
|
|
break_mins: i64,
|
|
|
|
|
state: TimerState,
|
|
|
|
|
prior_state: TimerState,
|
|
|
|
|
is_running: bool,
|
|
|
|
|
duration_in_secs: i64,
|
|
|
|
|
time_remaining_in_sec: i64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Termato {
|
|
|
|
|
fn new(work_mins: i64, break_mins: i64) -> Self {
|
|
|
|
|
Termato {
|
|
|
|
|
work_mins,
|
|
|
|
|
break_mins,
|
|
|
|
|
state: TimerState::Work,
|
|
|
|
|
prior_state: TimerState::Work,
|
|
|
|
|
is_running: false,
|
|
|
|
|
duration_in_secs: work_mins * SECONDS_PER_MIN,
|
|
|
|
|
time_remaining_in_sec: break_mins * SECONDS_PER_MIN,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn toggle_mode(&mut self) {
|
|
|
|
|
let is_on_break = self.state == TimerState::Break
|
|
|
|
|
|| (self.state == TimerState::Paused && self.prior_state == TimerState::Break);
|
|
|
|
|
|
|
|
|
|
if is_on_break {
|
|
|
|
|
self.state = TimerState::Work;
|
|
|
|
|
self.prior_state = TimerState::Work;
|
|
|
|
|
self.duration_in_secs = self.work_mins * SECONDS_PER_MIN;
|
|
|
|
|
} else {
|
|
|
|
|
self.state = TimerState::Break;
|
|
|
|
|
self.prior_state = TimerState::Break;
|
|
|
|
|
self.duration_in_secs = self.break_mins * SECONDS_PER_MIN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.time_remaining_in_sec = self.duration_in_secs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn tick(&mut self) {
|
|
|
|
|
if self.is_running && self.time_remaining_in_sec > 0 {
|
|
|
|
|
self.time_remaining_in_sec -= 1;
|
|
|
|
|
|
|
|
|
|
if self.time_remaining_in_sec == 0 {
|
|
|
|
|
self.toggle_mode();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-22 18:25:02 -05:00
|
|
|
fn main() {
|
2026-07-22 19:32:28 -05:00
|
|
|
let mut termato = Termato::new(1, 1);
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
termato.is_running = true;
|
|
|
|
|
|
|
|
|
|
termato.tick();
|
|
|
|
|
|
|
|
|
|
println!("Time remaining: {}", termato.time_remaining_in_sec);
|
|
|
|
|
println!("State: {:?}", termato.state);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn termato_when_called_it_should_return_expected_starting_state() {
|
|
|
|
|
let result = Termato::new(1, 1);
|
|
|
|
|
|
|
|
|
|
assert_eq!(result.work_mins, 1);
|
|
|
|
|
assert_eq!(result.break_mins, 1);
|
|
|
|
|
assert_eq!(result.state, TimerState::Work);
|
|
|
|
|
assert_eq!(result.prior_state, TimerState::Work);
|
|
|
|
|
assert!(!result.is_running);
|
|
|
|
|
assert_eq!(result.duration_in_secs, 60);
|
|
|
|
|
assert_eq!(result.time_remaining_in_sec, 60);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn termato_tick_when_called_and_not_running_it_should_do_nothing() {
|
|
|
|
|
let mut termato = Termato::new(1, 1);
|
|
|
|
|
|
|
|
|
|
termato.tick();
|
|
|
|
|
|
|
|
|
|
assert_eq!(termato.time_remaining_in_sec, 60);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn termato_tick_when_called_and_no_time_remaining_it_should_do_nothing() {
|
|
|
|
|
let mut termato = Termato::new(1, 1);
|
|
|
|
|
termato.is_running = true;
|
|
|
|
|
termato.time_remaining_in_sec = 0;
|
|
|
|
|
|
|
|
|
|
termato.tick();
|
|
|
|
|
|
|
|
|
|
assert_eq!(termato.time_remaining_in_sec, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn termato_tick_when_running_and_time_reamining_it_should_reduce_remaining_by_one() {
|
|
|
|
|
let mut termato = Termato::new(1, 1);
|
|
|
|
|
termato.is_running = true;
|
|
|
|
|
|
|
|
|
|
termato.tick();
|
|
|
|
|
|
|
|
|
|
assert_eq!(termato.time_remaining_in_sec, 59);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn termato_tick_when_running_on_work_and_time_remaining_reaches_zero_it_should_toggle_to_break() {
|
|
|
|
|
let mut termato = Termato::new(1, 2);
|
|
|
|
|
termato.is_running = true;
|
|
|
|
|
termato.time_remaining_in_sec = 1;
|
|
|
|
|
|
|
|
|
|
termato.tick();
|
|
|
|
|
|
|
|
|
|
assert_eq!(termato.state, TimerState::Break);
|
|
|
|
|
assert_eq!(termato.prior_state, TimerState::Break);
|
|
|
|
|
assert_eq!(termato.duration_in_secs, 120);
|
|
|
|
|
assert_eq!(termato.time_remaining_in_sec, 120);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn termato_tick_when_running_on_break_and_time_remaining_reaches_zero_it_should_toggle_to_work() {
|
|
|
|
|
let mut termato = Termato::new(2, 1);
|
|
|
|
|
termato.state = TimerState::Break;
|
|
|
|
|
termato.is_running = true;
|
|
|
|
|
termato.time_remaining_in_sec = 1;
|
|
|
|
|
|
|
|
|
|
termato.tick();
|
|
|
|
|
|
|
|
|
|
assert_eq!(termato.state, TimerState::Work);
|
|
|
|
|
assert_eq!(termato.prior_state, TimerState::Work);
|
|
|
|
|
assert_eq!(termato.duration_in_secs, 120);
|
|
|
|
|
assert_eq!(termato.time_remaining_in_sec, 120);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn termato_toggle_mode_when_paused_and_previous_state_is_break_it_should_toggle_to_work() {
|
|
|
|
|
let mut termato = Termato::new(2, 1);
|
|
|
|
|
termato.state = TimerState::Paused;
|
|
|
|
|
termato.prior_state = TimerState::Break;
|
|
|
|
|
|
|
|
|
|
termato.toggle_mode();
|
|
|
|
|
|
|
|
|
|
assert_eq!(termato.state, TimerState::Work);
|
|
|
|
|
assert_eq!(termato.prior_state, TimerState::Work);
|
|
|
|
|
assert_eq!(termato.duration_in_secs, 120);
|
|
|
|
|
assert_eq!(termato.time_remaining_in_sec, 120);
|
|
|
|
|
}
|
2026-07-22 18:25:02 -05:00
|
|
|
}
|