Refactor + Document

This commit is contained in:
Silas Bartha 2024-01-04 22:49:55 -05:00
parent 37c7a239b8
commit ad825d4830
Signed by: soaos
GPG Key ID: 9BD3DCC0D56A09B2
7 changed files with 179 additions and 167 deletions

2
Cargo.lock generated
View File

@ -959,7 +959,7 @@ dependencies = [
[[package]]
name = "pomd"
version = "1.3.0"
version = "1.4.0"
dependencies = [
"async-std",
"confy",

View File

@ -1,6 +1,6 @@
[package]
name = "pomd"
version = "1.3.0"
version = "1.4.0"
description = "A simple configurable pomodoro D-Bus daemon"
license = "MIT OR Apache-2.0"
edition = "2021"

View File

@ -175,18 +175,7 @@
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2023 Silas Bartha
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

28
src/config.rs Normal file
View File

@ -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,
}
}
}

55
src/interface.rs Normal file
View File

@ -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();
}
}

View File

@ -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)?

82
src/pomd.rs Normal file
View File

@ -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();
}
}