Refactor + Document
This commit is contained in:
parent
37c7a239b8
commit
ad825d4830
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -959,7 +959,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pomd"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"confy",
|
||||
|
@ -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"
|
||||
|
@ -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
28
src/config.rs
Normal 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
55
src/interface.rs
Normal 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();
|
||||
}
|
||||
}
|
164
src/main.rs
164
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)?
|
||||
|
82
src/pomd.rs
Normal file
82
src/pomd.rs
Normal 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();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user