aboutsummaryrefslogtreecommitdiffstats
path: root/internal/notify/config.go
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-19 09:15:38 +0200
committerDanilo M. <danix@danix.xyz>2026-10-19 09:15:38 +0200
commit75acc11af55b4efe0ea095a52a3916b13639c2cd (patch)
tree9e56593a3c6ef8aa7b6d03c9408d3239a1d8a582 /internal/notify/config.go
parenta8003f630728b8e4b5f2b163d9be2837da146b23 (diff)
downloadnotifyd-75acc11af55b4efe0ea095a52a3916b13639c2cd.tar.gz
notifyd-75acc11af55b4efe0ea095a52a3916b13639c2cd.zip
feat(notify): read timeouts and caps from config.json
A missing or broken file is the defaults, so a hand typo never stops the daemon; a key of the wrong type keeps its default while the rest applies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'internal/notify/config.go')
-rw-r--r--internal/notify/config.go91
1 files changed, 91 insertions, 0 deletions
diff --git a/internal/notify/config.go b/internal/notify/config.go
new file mode 100644
index 0000000..2b6ba5e
--- /dev/null
+++ b/internal/notify/config.go
@@ -0,0 +1,91 @@
+// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+package notify
+
+import (
+ "encoding/json"
+ "errors"
+ "log"
+ "os"
+ "path/filepath"
+)
+
+// Timeouts are the balloon lifetimes in seconds per urgency, used only when a
+// client leaves the choice to the server. 0 means never expires.
+type Timeouts struct {
+ Low int `json:"low"`
+ Normal int `json:"normal"`
+ Critical int `json:"critical"`
+}
+
+// Config is the daemon's share of ~/.config/notifyd/config.json. The file
+// also carries the balloon shell's keys (placement, DND, mute); they are not
+// declared here and the decoder ignores them.
+type Config struct {
+ Timeouts Timeouts `json:"timeouts"`
+ LiveCap int `json:"liveCap"`
+ HistoryCap int `json:"historyCap"`
+}
+
+// DefaultConfig is the behaviour before the file existed.
+func DefaultConfig() Config {
+ return Config{Timeouts: Timeouts{Low: 10, Normal: 10, Critical: 0}, LiveCap: 20, HistoryCap: 20}
+}
+
+// ConfigPath is $XDG_CONFIG_HOME/notifyd/config.json, ~/.config when unset.
+func ConfigPath() string {
+ dir, err := os.UserConfigDir()
+ if err != nil {
+ dir = filepath.Join(homeDir(), ".config")
+ }
+ return filepath.Join(dir, "notifyd", "config.json")
+}
+
+// LoadConfig never fails: a hand typo must not stop the daemon. A missing
+// file or broken JSON is the defaults; a key of the wrong type keeps its
+// default while the rest of the file applies, which is what encoding/json
+// does on an UnmarshalTypeError. Out-of-range values clamp.
+func LoadConfig(path string) Config {
+ def := DefaultConfig()
+ data, err := os.ReadFile(path)
+ if err != nil {
+ if !errors.Is(err, os.ErrNotExist) {
+ log.Printf("notifyd: config: %v", err)
+ }
+ return def
+ }
+ c := def
+ if err := json.Unmarshal(data, &c); err != nil {
+ var typeErr *json.UnmarshalTypeError
+ if !errors.As(err, &typeErr) {
+ log.Printf("notifyd: config %s: %v, using defaults", path, err)
+ return def
+ }
+ log.Printf("notifyd: config %s: %v", path, err)
+ }
+ c.LiveCap = clamp(c.LiveCap, 1, 100)
+ c.HistoryCap = clamp(c.HistoryCap, 1, 100)
+ if c.Timeouts.Low < 0 {
+ c.Timeouts.Low = def.Timeouts.Low
+ }
+ if c.Timeouts.Normal < 0 {
+ c.Timeouts.Normal = def.Timeouts.Normal
+ }
+ if c.Timeouts.Critical < 0 {
+ c.Timeouts.Critical = def.Timeouts.Critical
+ }
+ return c
+}
+
+func clamp(v, lo, hi int) int {
+ return min(max(v, lo), hi)
+}