git.lucas.co / cce-notifier
notification daemon
git clone https://git.lucas.co/cce-notifier.git

commit1f4354ed5425294c19005ec662ed57791da727ba
parent5f827b465d
authorLucas Galante <[email protected]>
date2026-07-06 21:43
Optimize config loading in cce-notifier using static cache

 Makefile             |  2 ++
 cce-notifier.service | 14 +++++++++
 src/main.rs          | 81 ++++++++++++++++++++++++++++++++++++++++------------
 3 files changed, 79 insertions(+), 18 deletions(-)

diff --git a/Makefile b/Makefile
index 9a1ab80..125908d 100644
--- a/Makefile
+++ b/Makefile
@@ -6,6 +6,8 @@ build:
 install: build
 	mkdir -p ~/.local/bin
 	install -m 755 ../target/release/cce-notifier ~/.local/bin/cce-notifier
+	mkdir -p ~/.config/systemd/user
+	install -m 644 cce-notifier.service ~/.config/systemd/user/cce-notifier.service
 
 run:
 	cargo run
diff --git a/cce-notifier.service b/cce-notifier.service
new file mode 100644
index 0000000..f8a90e1
--- /dev/null
+++ b/cce-notifier.service
@@ -0,0 +1,14 @@
+[Unit]
+Description=CCE Notification Daemon
+After=graphical-session.target
+PartOf=graphical-session.target
+
+[Service]
+Type=simple
+ExecStart=%h/.local/bin/cce-notifier
+Restart=on-failure
+RestartSec=3
+Environment=RUST_LOG=info
+
+[Install]
+WantedBy=graphical-session.target
diff --git a/src/main.rs b/src/main.rs
index 078ff20..561e48d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -449,41 +449,86 @@ impl NotificationApp {
     }
 }
 
+struct CachedConfig {
+    last_modified: Option<std::time::SystemTime>,
+    parsed: Option<serde_json::Value>,
+}
+
+static CONFIG_CACHE: std::sync::RwLock<CachedConfig> = std::sync::RwLock::new(CachedConfig {
+    last_modified: None,
+    parsed: None,
+});
+
+fn load_config() -> serde_json::Value {
+    let path = "/home/lsgalante/.config/cce/config.kdl";
+    let current_modified = std::fs::metadata(path).ok().and_then(|m| m.modified().ok());
+    
+    if let Ok(cache) = CONFIG_CACHE.read() {
+        if cache.last_modified.is_some() && cache.last_modified == current_modified {
+            if let Some(ref val) = cache.parsed {
+                return val.clone();
+            }
+        }
+    }
+    
+    let content = std::fs::read_to_string(path).unwrap_or_default();
+    let val = cce_ui::config::parse_kdl_to_json(&content);
+    if let Ok(mut cache) = CONFIG_CACHE.write() {
+        cache.last_modified = current_modified;
+        cache.parsed = Some(val.clone());
+    }
+    val
+}
+
 fn play_bell_if_configured() {
-    let config_path = "/home/lsgalante/.config/cce/config.json";
-    let content = std::fs::read_to_string(config_path).unwrap_or_default();
-    let val: serde_json::Value = serde_json::from_str(&content).unwrap_or_default();
-    let bell_enabled = val.pointer("/notifications/bell").and_then(|v| v.as_bool()).unwrap_or(false);
+    let val = load_config();
+    let sound_type = val.pointer("/notifications/bell").and_then(|v| v.as_str()).unwrap_or("none");
     
-    if bell_enabled {
-        log::info!("Playing notification bell sound...");
-        if let Err(e) = std::process::Command::new("pw-play")
-            .arg("/usr/share/sounds/freedesktop/stereo/bell.oga")
+    let sound_event = match sound_type {
+        "bell" => Some("bell"),
+        "dialog" => Some("dialog-information"),
+        "message" => Some("message"),
+        _ => None,
+    };
+    
+    if let Some(event) = sound_event {
+        log::info!("Playing notification sound ({})...", sound_type);
+        if std::process::Command::new("canberra-gtk-play")
+            .arg("-i")
+            .arg(event)
             .spawn()
+            .is_err()
         {
-            log::error!("Failed to spawn pw-play: {}", e);
+            let sound_file = match sound_type {
+                "bell" => "/usr/share/sounds/freedesktop/stereo/bell.oga",
+                "dialog" => "/usr/share/sounds/freedesktop/stereo/dialog-information.oga",
+                "message" => "/usr/share/sounds/freedesktop/stereo/message.oga",
+                _ => "",
+            };
+            if !sound_file.is_empty() {
+                if let Err(e) = std::process::Command::new("pw-play")
+                    .arg(sound_file)
+                    .spawn()
+                {
+                    log::error!("Failed to spawn pw-play: {}", e);
+                }
+            }
         }
     }
 }
 
 fn read_duration_if_configured() -> u64 {
-    let config_path = "/home/lsgalante/.config/cce/config.json";
-    let content = std::fs::read_to_string(config_path).unwrap_or_default();
-    let val: serde_json::Value = serde_json::from_str(&content).unwrap_or_default();
+    let val = load_config();
     val.pointer("/notifications/duration").and_then(|v| v.as_u64()).unwrap_or(5)
 }
 
 fn read_opacity_if_configured() -> f32 {
-    let config_path = "/home/lsgalante/.config/cce/config.json";
-    let content = std::fs::read_to_string(config_path).unwrap_or_default();
-    let val: serde_json::Value = serde_json::from_str(&content).unwrap_or_default();
+    let val = load_config();
     val.pointer("/notifications/opacity").and_then(|v| v.as_f64()).map(|n| n as f32).unwrap_or(0.9)
 }
 
 fn read_bg_color_if_configured() -> [f32; 4] {
-    let config_path = "/home/lsgalante/.config/cce/config.json";
-    let content = std::fs::read_to_string(config_path).unwrap_or_default();
-    let val: serde_json::Value = serde_json::from_str(&content).unwrap_or_default();
+    let val = load_config();
     
     if let Some(hex_str) = val.pointer("/notifications/bg_color").and_then(|v| v.as_str()) {
         let hex = hex_str.trim_matches(|c| c == '"' || c == '\'' || c == ' ').trim_start_matches('#');