From 901d27db601a27d580c1f2f8d4b3d3361f60cc2e Mon Sep 17 00:00:00 2001
From: Silas Bartha <silas@exvacuum.dev>
Date: Thu, 21 Dec 2023 23:36:07 -0500
Subject: [PATCH] 1.2.0 - configuration file support

---
 Cargo.lock  | 53 ++++++++++++++++++++++++++++++++++++++----
 Cargo.toml  |  4 +++-
 README.md   |  4 ++++
 src/main.rs | 66 +++++++++++++++++++++++++++++++++++++++--------------
 4 files changed, 104 insertions(+), 23 deletions(-)
 create mode 100644 README.md

diff --git a/Cargo.lock b/Cargo.lock
index 64d30fe..6d36301 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -324,6 +324,18 @@ dependencies = [
  "crossbeam-utils",
 ]
 
+[[package]]
+name = "confy"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e37668cb35145dcfaa1931a5f37fde375eeae8068b4c0d2f289da28a270b2d2c"
+dependencies = [
+ "directories",
+ "serde",
+ "thiserror",
+ "toml",
+]
+
 [[package]]
 name = "cpufeatures"
 version = "0.2.11"
@@ -382,6 +394,15 @@ dependencies = [
  "crypto-common",
 ]
 
+[[package]]
+name = "directories"
+version = "4.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210"
+dependencies = [
+ "dirs-sys",
+]
+
 [[package]]
 name = "dirs-next"
 version = "2.0.0"
@@ -392,6 +413,17 @@ dependencies = [
  "dirs-sys-next",
 ]
 
+[[package]]
+name = "dirs-sys"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
 [[package]]
 name = "dirs-sys-next"
 version = "0.1.2"
@@ -927,11 +959,13 @@ dependencies = [
 
 [[package]]
 name = "pomd"
-version = "1.1.0"
+version = "1.2.0"
 dependencies = [
  "async-std",
+ "confy",
  "notify-rust",
  "pausable_clock",
+ "serde",
  "zbus",
 ]
 
@@ -1119,18 +1153,18 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
 
 [[package]]
 name = "serde"
-version = "1.0.192"
+version = "1.0.193"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
+checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.192"
+version = "1.0.193"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
+checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1301,6 +1335,15 @@ version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
 
+[[package]]
+name = "toml"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "toml_datetime"
 version = "0.6.5"
diff --git a/Cargo.toml b/Cargo.toml
index f4a084d..790d314 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "pomd"
-version = "1.1.1"
+version = "1.2.0"
 edition = "2021"
 
 [dependencies]
@@ -8,3 +8,5 @@ notify-rust = "4"
 zbus = "3.14.1"
 async-std = { version = "1.12.0", features = ["attributes"] }
 pausable_clock = "1.0.1"
+confy = "0.5.1"
+serde = "1.0.193"
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d2faf00
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+# pomd: pomodoro daemon
+
+This program provides a simple pomodoro daemon for linux.
+
diff --git a/src/main.rs b/src/main.rs
index b34d17d..af1eea7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,16 +4,35 @@ use std::{
 };
 
 use pausable_clock::{PausableClock, PausableInstant};
+use serde::{Serialize, Deserialize};
 use zbus::{dbus_interface, ConnectionBuilder, Result};
 
 use notify_rust::Notification;
 
-const WORK_DURATION_SECS: f32 = 15.0 * 60.0;
-const SHORT_BREAK_DURATION_SECS: f32 = 5.0 * 60.0;
-const LONG_BREAK_DURATION_SECS: f32 = 25.0 * 60.0;
-const NUM_ITERATIONS: u8 = 4;
+#[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,
@@ -21,9 +40,18 @@ struct Pomd {
     start: PausableInstant
 }
 
-#[derive(Default)]
 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")]
@@ -54,7 +82,7 @@ impl PomdInterface {
     }
 
     async fn stop(&self) {
-        *self.data.lock().unwrap() = Pomd::default();
+        *self.data.lock().unwrap() = Pomd::new(self.config);
     }
 
     async fn skip(&self) {
@@ -62,12 +90,13 @@ impl PomdInterface {
     }
 }
 
-impl Default for Pomd {
-    fn default() -> Self {
+impl  Pomd {
+    fn new(config: PomdConfig) -> Self {
         let clock = PausableClock::new(Duration::ZERO, true);
         let start  = clock.now();
         Self {
-            duration: Duration::from_secs_f32(WORK_DURATION_SECS),
+            config,
+            duration: Duration::from_secs_f32(config.work_duration),
             iteration: 0,
             on_break: false,
             clock,
@@ -79,7 +108,9 @@ impl Default for Pomd {
 impl Pomd {
     fn update(&mut self) {
         if self.duration < self.start.elapsed(&self.clock) {
-                self.notify();
+                if self.config.notify {
+                    self.notify();
+                }
                 self.setup_next_iteration();
         }
     }
@@ -89,14 +120,14 @@ impl Pomd {
         self.start  = self.clock.now();
         self.on_break ^= true;
         self.duration = if self.on_break {
-            if self.iteration == NUM_ITERATIONS - 1 {
-                Duration::from_secs_f32(LONG_BREAK_DURATION_SECS)
+            if self.iteration == self.config.num_iterations - 1 {
+                Duration::from_secs_f32(self.config.long_break_duration)
             } else {
-                Duration::from_secs_f32(SHORT_BREAK_DURATION_SECS)
+                Duration::from_secs_f32(self.config.short_break_duration)
             }
         } else {
-            self.iteration = (self.iteration + 1) % NUM_ITERATIONS;
-            Duration::from_secs_f32(WORK_DURATION_SECS)
+            self.iteration = (self.iteration + 1) % self.config.num_iterations;
+            Duration::from_secs_f32(self.config.work_duration)
         }
     }
 
@@ -112,7 +143,7 @@ impl Pomd {
                 .summary(&format!(
                     "Pomodoro Complete ({}/{})",
                     self.iteration + 1,
-                    NUM_ITERATIONS
+                    self.config.num_iterations
                 ))
                 .body("Click to dismiss")
                 .show()
@@ -123,7 +154,8 @@ impl Pomd {
 
 #[async_std::main]
 async fn main() -> Result<()> {
-    let pomd_interface = PomdInterface::default();
+    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 _connection = ConnectionBuilder::session()?
         .name("dev.exvacuum.pomd")?