refactor: extract termato struct and implementations
This commit is contained in:
+285
@@ -0,0 +1,285 @@
|
||||
use crate::msg::Message;
|
||||
use crate::notification::send_desktop_notification;
|
||||
|
||||
const SECONDS_PER_MIN: u32 = 60;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum TimerMode {
|
||||
Work,
|
||||
Break,
|
||||
}
|
||||
|
||||
pub struct Termato {
|
||||
pub work_mins: u32,
|
||||
pub break_mins: u32,
|
||||
pub mode: TimerMode,
|
||||
pub is_running: bool,
|
||||
pub duration_in_secs: u32,
|
||||
pub time_remaining_in_sec: u32,
|
||||
pub show_help: bool,
|
||||
pub enable_notifications: bool,
|
||||
pub enable_visualizer: bool,
|
||||
pub should_quit: bool,
|
||||
}
|
||||
|
||||
impl Termato {
|
||||
pub fn new(work_mins: u32, break_mins: u32) -> Self {
|
||||
Termato {
|
||||
work_mins,
|
||||
break_mins,
|
||||
mode: TimerMode::Work,
|
||||
is_running: false,
|
||||
duration_in_secs: work_mins * SECONDS_PER_MIN,
|
||||
time_remaining_in_sec: work_mins * SECONDS_PER_MIN,
|
||||
show_help: false,
|
||||
enable_notifications: false,
|
||||
enable_visualizer: false,
|
||||
should_quit: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_notifications(mut self, enable: bool) -> Self {
|
||||
self.enable_notifications = enable;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_visualizer(mut self, enable: bool) -> Self {
|
||||
self.enable_visualizer = enable;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn update(&mut self, msg: Message) {
|
||||
match msg {
|
||||
Message::Tick => self.tick(),
|
||||
Message::ToggleRunning => self.toggle_running(),
|
||||
Message::ToggleMode => self.toggle_mode(),
|
||||
Message::Reset => self.reset(),
|
||||
Message::ToggleHelp => self.toggle_help(),
|
||||
Message::Quit => self.should_quit = true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle_running(&mut self) {
|
||||
self.is_running = !self.is_running;
|
||||
}
|
||||
|
||||
pub fn toggle_mode(&mut self) {
|
||||
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;
|
||||
}
|
||||
|
||||
pub 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 {
|
||||
if self.enable_notifications {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.is_running = false;
|
||||
self.mode = TimerMode::Work;
|
||||
self.duration_in_secs = self.work_mins * SECONDS_PER_MIN;
|
||||
self.time_remaining_in_sec = self.duration_in_secs;
|
||||
}
|
||||
|
||||
pub fn toggle_help(&mut self) {
|
||||
self.show_help = !self.show_help;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn termato_new_when_called_it_should_return_expected_starting_state() {
|
||||
let result = Termato::new(25, 5);
|
||||
|
||||
assert_eq!(result.work_mins, 25);
|
||||
assert_eq!(result.break_mins, 5);
|
||||
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);
|
||||
assert!(!result.show_help);
|
||||
assert!(!result.enable_notifications);
|
||||
assert!(!result.enable_visualizer);
|
||||
assert!(!result.should_quit);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn termato_update_when_called_with_messages_it_should_update_state() {
|
||||
let mut termato = Termato::new(25, 5);
|
||||
|
||||
termato.update(Message::ToggleRunning);
|
||||
assert!(termato.is_running);
|
||||
|
||||
termato.update(Message::ToggleHelp);
|
||||
assert!(termato.show_help);
|
||||
|
||||
termato.update(Message::ToggleMode);
|
||||
assert_eq!(termato.mode, TimerMode::Break);
|
||||
|
||||
termato.update(Message::Reset);
|
||||
assert!(!termato.is_running);
|
||||
assert_eq!(termato.mode, TimerMode::Work);
|
||||
|
||||
termato.update(Message::Quit);
|
||||
assert!(termato.should_quit);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn termato_tick_when_called_and_not_running_it_should_do_nothing() {
|
||||
let mut termato = Termato::new(1, 1);
|
||||
|
||||
termato.update(Message::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.update(Message::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.update(Message::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.update(Message::Tick);
|
||||
|
||||
assert_eq!(termato.mode, TimerMode::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.mode = TimerMode::Break;
|
||||
termato.is_running = true;
|
||||
termato.time_remaining_in_sec = 1;
|
||||
|
||||
termato.update(Message::Tick);
|
||||
|
||||
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_called_it_should_toggle_the_mode() {
|
||||
let mut termato = Termato::new(2, 1);
|
||||
|
||||
termato.toggle_mode();
|
||||
assert_eq!(termato.mode, TimerMode::Break);
|
||||
assert_eq!(termato.duration_in_secs, 60);
|
||||
assert_eq!(termato.time_remaining_in_sec, 60);
|
||||
|
||||
termato.toggle_mode();
|
||||
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_running_when_called_and_already_running_it_should_pause() {
|
||||
let mut termato = Termato::new(1, 1);
|
||||
termato.is_running = true;
|
||||
|
||||
termato.toggle_running();
|
||||
|
||||
assert!(!termato.is_running)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn termato_toggle_running_when_called_and_already_paused_it_should_run() {
|
||||
let mut termato = Termato::new(1, 1);
|
||||
|
||||
termato.toggle_running();
|
||||
|
||||
assert!(termato.is_running)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn termato_rest_when_called_it_should_reset_the_apps_state() {
|
||||
let mut termato = Termato::new(1, 1);
|
||||
termato.is_running = true;
|
||||
termato.mode = TimerMode::Break;
|
||||
termato.duration_in_secs = 1;
|
||||
termato.time_remaining_in_sec = 1;
|
||||
|
||||
termato.reset();
|
||||
|
||||
assert!(!termato.is_running);
|
||||
assert_eq!(termato.mode, TimerMode::Work);
|
||||
assert_eq!(termato.duration_in_secs, 60);
|
||||
assert_eq!(termato.time_remaining_in_sec, 60);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn termato_toggle_help_when_called_it_should_toggle_help() {
|
||||
let mut termato = Termato::new(1, 1);
|
||||
|
||||
termato.toggle_help();
|
||||
|
||||
assert!(termato.show_help);
|
||||
|
||||
termato.toggle_help();
|
||||
|
||||
assert!(!termato.show_help);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn termato_notifications_when_called_it_should_verify_opt_in_behavior() {
|
||||
let result = Termato::new(25, 5);
|
||||
assert!(!result.enable_notifications);
|
||||
|
||||
let result = result.with_notifications(true);
|
||||
assert!(result.enable_notifications);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn termato_visualizer_when_called_it_should_verify_opt_in_behavior() {
|
||||
let result = Termato::new(25, 5);
|
||||
assert!(!result.enable_visualizer);
|
||||
|
||||
let result = result.with_visualizer(true);
|
||||
assert!(result.enable_visualizer);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user