refactor: simplify state machine.
This commit is contained in:
+42
-64
@@ -21,13 +21,12 @@ use ratatui::{
|
||||
widgets::{Block, Borders, Clear, Paragraph},
|
||||
};
|
||||
|
||||
const SECONDS_PER_MIN: i64 = 60;
|
||||
const SECONDS_PER_MIN: u32 = 60;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
enum TimerState {
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
enum TimerMode {
|
||||
Work,
|
||||
Break,
|
||||
Paused,
|
||||
}
|
||||
|
||||
fn send_desktop_notification(title: &str, body: &str) {
|
||||
@@ -44,23 +43,21 @@ fn send_desktop_notification(title: &str, body: &str) {
|
||||
}
|
||||
|
||||
struct Termato {
|
||||
work_mins: i64,
|
||||
break_mins: i64,
|
||||
state: TimerState,
|
||||
prior_state: TimerState,
|
||||
work_mins: u32,
|
||||
break_mins: u32,
|
||||
mode: TimerMode,
|
||||
is_running: bool,
|
||||
duration_in_secs: i64,
|
||||
time_remaining_in_sec: i64,
|
||||
duration_in_secs: u32,
|
||||
time_remaining_in_sec: u32,
|
||||
show_help: bool,
|
||||
}
|
||||
|
||||
impl Termato {
|
||||
fn new(work_mins: i64, break_mins: i64) -> Self {
|
||||
fn new(work_mins: u32, break_mins: u32) -> Self {
|
||||
Termato {
|
||||
work_mins,
|
||||
break_mins,
|
||||
state: TimerState::Work,
|
||||
prior_state: TimerState::Work,
|
||||
mode: TimerMode::Work,
|
||||
is_running: false,
|
||||
duration_in_secs: work_mins * SECONDS_PER_MIN,
|
||||
time_remaining_in_sec: work_mins * SECONDS_PER_MIN,
|
||||
@@ -69,30 +66,18 @@ impl Termato {
|
||||
}
|
||||
|
||||
fn toggle_running(&mut self) {
|
||||
if self.is_running {
|
||||
self.prior_state = self.state.clone();
|
||||
self.state = TimerState::Paused;
|
||||
self.is_running = false;
|
||||
} else {
|
||||
self.state = self.prior_state.clone();
|
||||
self.is_running = true;
|
||||
}
|
||||
self.is_running = !self.is_running;
|
||||
}
|
||||
|
||||
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.mode = match self.mode {
|
||||
TimerMode::Work => TimerMode::Break,
|
||||
TimerMode::Break => TimerMode::Work,
|
||||
};
|
||||
self.duration_in_secs = match self.mode {
|
||||
TimerMode::Work => self.work_mins * SECONDS_PER_MIN,
|
||||
TimerMode::Break => self.break_mins * SECONDS_PER_MIN,
|
||||
};
|
||||
self.time_remaining_in_sec = self.duration_in_secs;
|
||||
}
|
||||
|
||||
@@ -101,10 +86,9 @@ impl Termato {
|
||||
self.time_remaining_in_sec -= 1;
|
||||
|
||||
if self.time_remaining_in_sec == 0 {
|
||||
if self.state == TimerState::Work {
|
||||
send_desktop_notification("Work Done", "Time to take a break.");
|
||||
} else {
|
||||
send_desktop_notification("Break Over", "Time to focus.");
|
||||
match self.mode {
|
||||
TimerMode::Work => send_desktop_notification("Work Done", "Time to take a break."),
|
||||
TimerMode::Break => send_desktop_notification("Break Over", "Time to focus."),
|
||||
}
|
||||
|
||||
self.toggle_mode();
|
||||
@@ -114,8 +98,7 @@ impl Termato {
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.is_running = false;
|
||||
self.state = TimerState::Work;
|
||||
self.prior_state = TimerState::Work;
|
||||
self.mode = TimerMode::Work;
|
||||
self.duration_in_secs = self.work_mins * SECONDS_PER_MIN;
|
||||
self.time_remaining_in_sec = self.duration_in_secs;
|
||||
}
|
||||
@@ -169,10 +152,13 @@ fn main() -> Result<(), io::Error> {
|
||||
let seconds = termato.time_remaining_in_sec % SECONDS_PER_MIN;
|
||||
let time_str = format!("{:02}:{:02}", minutes, seconds);
|
||||
|
||||
let time_color = match termato.state {
|
||||
TimerState::Work => Color::Red,
|
||||
TimerState::Break => Color::Green,
|
||||
TimerState::Paused => Color::Yellow,
|
||||
let time_color = if !termato.is_running {
|
||||
Color::Yellow
|
||||
} else {
|
||||
match termato.mode {
|
||||
TimerMode::Work => Color::Red,
|
||||
TimerMode::Break => Color::Green,
|
||||
}
|
||||
};
|
||||
|
||||
let big_time_text = if let Some(fig) = font.convert(&time_str) {
|
||||
@@ -278,8 +264,7 @@ mod tests {
|
||||
|
||||
assert_eq!(result.work_mins, 25);
|
||||
assert_eq!(result.break_mins, 5);
|
||||
assert_eq!(result.state, TimerState::Work);
|
||||
assert_eq!(result.prior_state, TimerState::Work);
|
||||
assert_eq!(result.mode, TimerMode::Work);
|
||||
assert!(!result.is_running);
|
||||
assert_eq!(result.duration_in_secs, 1500);
|
||||
assert_eq!(result.time_remaining_in_sec, 1500);
|
||||
@@ -323,8 +308,7 @@ mod tests {
|
||||
|
||||
termato.tick();
|
||||
|
||||
assert_eq!(termato.state, TimerState::Break);
|
||||
assert_eq!(termato.prior_state, TimerState::Break);
|
||||
assert_eq!(termato.mode, TimerMode::Break);
|
||||
assert_eq!(termato.duration_in_secs, 120);
|
||||
assert_eq!(termato.time_remaining_in_sec, 120);
|
||||
}
|
||||
@@ -332,28 +316,28 @@ mod tests {
|
||||
#[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.mode = TimerMode::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.mode, TimerMode::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() {
|
||||
fn termato_toggle_mode_when_called_it_should_toggle_the_mode() {
|
||||
let mut termato = Termato::new(2, 1);
|
||||
termato.state = TimerState::Paused;
|
||||
termato.prior_state = TimerState::Break;
|
||||
|
||||
termato.toggle_mode();
|
||||
assert_eq!(termato.mode, TimerMode::Break);
|
||||
assert_eq!(termato.duration_in_secs, 60);
|
||||
assert_eq!(termato.time_remaining_in_sec, 60);
|
||||
|
||||
assert_eq!(termato.state, TimerState::Work);
|
||||
assert_eq!(termato.prior_state, TimerState::Work);
|
||||
termato.toggle_mode();
|
||||
assert_eq!(termato.mode, TimerMode::Work);
|
||||
assert_eq!(termato.duration_in_secs, 120);
|
||||
assert_eq!(termato.time_remaining_in_sec, 120);
|
||||
}
|
||||
@@ -365,8 +349,6 @@ mod tests {
|
||||
|
||||
termato.toggle_running();
|
||||
|
||||
assert_eq!(termato.state, TimerState::Paused);
|
||||
assert_eq!(termato.prior_state, TimerState::Work);
|
||||
assert!(!termato.is_running)
|
||||
}
|
||||
|
||||
@@ -376,8 +358,6 @@ mod tests {
|
||||
|
||||
termato.toggle_running();
|
||||
|
||||
assert_eq!(termato.state, TimerState::Work);
|
||||
assert_eq!(termato.prior_state, TimerState::Work);
|
||||
assert!(termato.is_running)
|
||||
}
|
||||
|
||||
@@ -385,16 +365,14 @@ mod tests {
|
||||
fn termato_rest_when_called_it_should_reset_the_apps_state() {
|
||||
let mut termato = Termato::new(1, 1);
|
||||
termato.is_running = true;
|
||||
termato.state = TimerState::Break;
|
||||
termato.prior_state = TimerState::Break;
|
||||
termato.mode = TimerMode::Break;
|
||||
termato.duration_in_secs = 1;
|
||||
termato.time_remaining_in_sec = 1;
|
||||
|
||||
termato.reset();
|
||||
|
||||
assert!(!termato.is_running);
|
||||
assert_eq!(termato.state, TimerState::Work);
|
||||
assert_eq!(termato.prior_state, TimerState::Work);
|
||||
assert_eq!(termato.mode, TimerMode::Work);
|
||||
assert_eq!(termato.duration_in_secs, 60);
|
||||
assert_eq!(termato.time_remaining_in_sec, 60);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user