/* Polydoro - Pomodoro widget for polybar and friends * Copyright (C) 2025 Vidhu Kant Sharma * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ use bincode::{Encode, Decode}; #[derive(Encode, Decode, PartialEq, Clone, Copy)] pub enum State { WorkIdle, BreakIdle, LongBreakIdle, Work, Break, LongBreak, WorkPaused, BreakPaused, LongBreakPaused, } #[derive(Encode, Decode)] pub struct Pomo { work_duration: u64, break_duration: u64, long_break_duration: u64, long_break_interval: u64, // only these are supposed to change pub secs_elapsed: u64, pub counter: u64, pub current_state: State, } impl Pomo { pub fn new() -> Self { Pomo { secs_elapsed: 0, counter: 1, current_state: State::WorkIdle, // TODO: load from config, probably work_duration: 25,// * 60, break_duration: 5,// * 60, long_break_duration: 20,// * 60, long_break_interval: 4, } } pub fn tick(&mut self) { // only tick on running states if matches!(self.current_state, State::Work | State::Break | State::LongBreak) { self.secs_elapsed += 1; let duration = match self.current_state { State::Work => self.work_duration, State::Break => self.break_duration, State::LongBreak => self.long_break_duration, _ => 0, // will never hit this case }; if self.secs_elapsed >= duration { self.end_cycle(); } } } // start/unpause a cycle pub fn run(&mut self) { self.current_state = match self.current_state { State::WorkIdle => State::Work, State::BreakIdle => State::Break, State::LongBreakIdle => State::LongBreak, State::WorkPaused => State::Work, State::BreakPaused => State::Break, State::LongBreakPaused => State::LongBreak, _ => return // already running }; } pub fn pause(&mut self) { self.current_state = match self.current_state { State::Work => State::WorkPaused, State::Break => State::BreakPaused, State::LongBreak => State::LongBreakPaused, _ => return, // idle and paused don't have a place here }; } // conditionally pause/run pub fn toggle(&mut self) { match self.current_state { State::Work | State::Break | State::LongBreak => self.pause(), _ => self.run(), } } // also used in the skip command pub fn end_cycle(&mut self) { match self.current_state { State::Work => { self.secs_elapsed = 0; self.current_state = if self.counter == self.long_break_interval { State::LongBreakIdle } else { State::BreakIdle }; }, State::Break => { self.secs_elapsed = 0; self.current_state = State::WorkIdle; // because we move on to the next task after the break self.counter += 1; } State::LongBreak => self.hard_reset(), _ => return, // paused and idle state can't be ended } } // reset current cycle pub fn soft_reset(&mut self) { self.secs_elapsed = 0; self.current_state = match self.current_state { State::Work | State::WorkPaused => State::WorkIdle, State::Break | State::BreakPaused => State::BreakIdle, State::LongBreak | State::LongBreakPaused => State::LongBreakIdle, _ => self.current_state, // if already in an idle state, don't change (probably won't hit anyways) }; } // reset all cycles pub fn hard_reset(&mut self) { self.secs_elapsed = 0; self.counter = 1; self.current_state = State::WorkIdle; } // conditionally do a soft or a hard reset pub fn reset(&mut self) { match self.current_state { State::WorkIdle | State::BreakIdle | State::LongBreakIdle => self.hard_reset(), _ => self.soft_reset(), } } }