diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/config.rs | 28 | ||||
-rw-r--r-- | src/interface.rs | 55 | ||||
-rw-r--r-- | src/main.rs | 164 | ||||
-rw-r--r-- | src/pomd.rs | 82 |
4 files changed, 176 insertions, 153 deletions
diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..a14ff7c --- /dev/null +++ b/src/config.rs @@ -0,0 +1,28 @@ +use serde::{Serialize, Deserialize}; + +/// Configuration for program +#[derive(Serialize, Deserialize, Clone, Copy)] +pub struct PomdConfig { + /// Length of work phases in seconds + pub work_duration: f32, + /// Length of short breaks in seconds + pub short_break_duration: f32, + /// Length of long breaks in seconds + pub long_break_duration: f32, + /// Number of iterations between long breaks + pub num_iterations: u8, + /// Whether to show system notifications + pub notify: bool, +} + +impl Default for PomdConfig { + fn default() -> Self { + Self { + work_duration: 15.0 * 60.0, + short_break_duration: 5.0 * 60.0, + long_break_duration: 25.0 * 60.0, + num_iterations: 4, + notify: true, + } + } +} diff --git a/src/interface.rs b/src/interface.rs new file mode 100644 index 0000000..870162c --- /dev/null +++ b/src/interface.rs @@ -0,0 +1,55 @@ +use std::{sync::{Arc, Mutex}, time::Duration}; +use zbus::dbus_interface; + +use crate::pomd::Pomd; + +/// D-Bus interface for the program +pub struct PomdInterface { + pub state: Arc<Mutex<Pomd>>, +} + +impl PomdInterface { + /// Create a new instance of the interface with a reference to the program state + pub fn new(state: Arc<Mutex<Pomd>>) -> Self { + Self { + state + } + } +} + +#[dbus_interface(name = "dev.exvacuum.pomd")] +impl PomdInterface { + fn get_remaining(&self) -> Duration { + let data = self.state.lock().unwrap(); + data.duration.checked_sub(data.start.elapsed(&data.clock)).unwrap_or_default() + } + + fn get_iteration(&self) -> u8 { + self.state.lock().unwrap().iteration + } + + fn is_running(&self) -> bool { + !self.state.lock().unwrap().clock.is_paused() + } + + fn is_on_break(&self) -> bool { + self.state.lock().unwrap().on_break + } + + fn start(&self) { + self.state.lock().unwrap().clock.resume(); + } + + fn pause(&self) { + self.state.lock().unwrap().clock.pause(); + } + + fn stop(&self) { + let mut data = self.state.lock().unwrap(); + *data = Pomd::new(data.config); + } + + fn skip(&self) { + self.state.lock().unwrap().setup_next_iteration(); + } +} diff --git a/src/main.rs b/src/main.rs index 992748c..3a8bcb0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,162 +1,20 @@ -use std::{ - sync::{Arc, Mutex}, - time::Duration, thread::sleep, -}; +use std::sync::{Mutex, Arc}; +use std::{thread::sleep, time::Duration}; +use zbus::{ConnectionBuilder, Result}; -use pausable_clock::{PausableClock, PausableInstant}; -use serde::{Serialize, Deserialize}; -use zbus::{dbus_interface, ConnectionBuilder, Result}; +use crate::config::PomdConfig; +use crate::interface::PomdInterface; +use crate::pomd::Pomd; -use notify_rust::Notification; - -#[derive(Serialize, Deserialize, Clone, Copy)] -struct PomdConfig { - work_duration: f32, - short_break_duration: f32, - long_break_duration: f32, - num_iterations: u8, - notify: bool, -} - -impl Default for PomdConfig { - fn default() -> Self { - Self { - work_duration: 15.0 * 60.0, - short_break_duration: 5.0 * 60.0, - long_break_duration: 25.0 * 60.0, - num_iterations: 4, - notify: true, - } - } -} - - -struct Pomd { - config: PomdConfig, - duration: Duration, - iteration: u8, - on_break: bool, - clock: PausableClock, - start: PausableInstant -} - -struct PomdInterface { - data: Arc<Mutex<Pomd>>, - config: PomdConfig -} - -impl PomdInterface { - fn new(config: PomdConfig) -> Self { - Self { - data: Arc::new(Mutex::new(Pomd::new(config))), - config, - } - } -} - -#[dbus_interface(name = "dev.exvacuum.pomd")] -impl PomdInterface { - async fn get_remaining(&self) -> Duration { - let data = self.data.lock().unwrap(); - data.duration.checked_sub(data.start.elapsed(&data.clock)).unwrap_or_default() - } - - async fn get_iteration(&self) -> u8 { - self.data.lock().unwrap().iteration - } - - async fn is_running(&self) -> bool { - !self.data.lock().unwrap().clock.is_paused() - } - - async fn is_on_break(&self) -> bool { - self.data.lock().unwrap().on_break - } - - async fn start(&self) { - self.data.lock().unwrap().clock.resume(); - } - - async fn pause(&self) { - self.data.lock().unwrap().clock.pause(); - } - - async fn stop(&self) { - *self.data.lock().unwrap() = Pomd::new(self.config); - } - - async fn skip(&self) { - self.data.lock().unwrap().setup_next_iteration(); - } -} - -impl Pomd { - fn new(config: PomdConfig) -> Self { - let clock = PausableClock::new(Duration::ZERO, true); - let start = clock.now(); - Self { - config, - duration: Duration::from_secs_f32(config.work_duration), - iteration: 0, - on_break: false, - clock, - start, - } - } -} - -impl Pomd { - fn update(&mut self) { - if self.duration < self.start.elapsed(&self.clock) { - if self.config.notify { - self.notify(); - } - self.setup_next_iteration(); - } - } - - fn setup_next_iteration(&mut self) { - self.clock.pause(); - self.start = self.clock.now(); - self.on_break ^= true; - self.duration = if self.on_break { - if self.iteration == self.config.num_iterations - 1 { - Duration::from_secs_f32(self.config.long_break_duration) - } else { - Duration::from_secs_f32(self.config.short_break_duration) - } - } else { - self.iteration = (self.iteration + 1) % self.config.num_iterations; - Duration::from_secs_f32(self.config.work_duration) - } - } - - fn notify(&self) { - if self.on_break { - Notification::new() - .summary("Break Complete") - .body("Click to dismiss") - .show() - .unwrap(); - } else { - Notification::new() - .summary(&format!( - "Pomodoro Complete ({}/{})", - self.iteration + 1, - self.config.num_iterations - )) - .body("Click to dismiss") - .show() - .unwrap(); - } - } -} +mod config; +mod interface; +mod pomd; #[async_std::main] async fn main() -> Result<()> { let config: PomdConfig = confy::load("pomd", "config").expect("Failed to load config!"); - let pomd_interface = PomdInterface::new(config); - let pomd = pomd_interface.data.clone(); + let pomd = Arc::new(Mutex::new(Pomd::new(config))); + let pomd_interface = PomdInterface::new(pomd.clone()); let _connection = ConnectionBuilder::session()? .name("dev.exvacuum.pomd")? .serve_at("/dev/exvacuum/pomd", pomd_interface)? diff --git a/src/pomd.rs b/src/pomd.rs new file mode 100644 index 0000000..5a6679f --- /dev/null +++ b/src/pomd.rs @@ -0,0 +1,82 @@ +use std::time::Duration; + +use pausable_clock::{PausableClock, PausableInstant}; + +use notify_rust::Notification; + +use crate::config::PomdConfig; + +/// Represents the current state of the program +pub struct Pomd { + pub config: PomdConfig, + pub duration: Duration, + pub iteration: u8, + pub on_break: bool, + pub clock: PausableClock, + pub start: PausableInstant, +} + +impl Pomd { + /// Creates a new instance of this struct with a given configuration + pub fn new(config: PomdConfig) -> Self { + let clock = PausableClock::new(Duration::ZERO, true); + let start = clock.now(); + Self { + config, + duration: Duration::from_secs_f32(config.work_duration), + iteration: 0, + on_break: false, + clock, + start, + } + } + + /// Check whether sufficient time has elapsed to enter next iteration of cycle + pub fn update(&mut self) { + if self.duration < self.start.elapsed(&self.clock) { + if self.config.notify { + self.notify(); + } + self.setup_next_iteration(); + } + } + + /// Resets state for next iteration + pub fn setup_next_iteration(&mut self) { + // Stop clock until user restarts it + self.clock.pause(); + + self.start = self.clock.now(); + self.on_break ^= true; + self.duration = if self.on_break { + // Long break on last iteration + if self.iteration == self.config.num_iterations - 1 { + Duration::from_secs_f32(self.config.long_break_duration) + } else { + Duration::from_secs_f32(self.config.short_break_duration) + } + } else { + self.iteration = (self.iteration + 1) % self.config.num_iterations; + Duration::from_secs_f32(self.config.work_duration) + } + } + + /// Displays a system notification + pub fn notify(&self) { + Notification::new() + .summary( + &(if self.on_break { + "Break Complete".to_string() + } else { + format!( + "Pomodoro Complete ({}/{})", + self.iteration + 1, + self.config.num_iterations + ) + }), + ) + .body("Click to dismiss") + .show() + .unwrap(); + } +} |