aboutsummaryrefslogtreecommitdiffstats
path: root/sb_builder/config.py
diff options
context:
space:
mode:
Diffstat (limited to 'sb_builder/config.py')
-rw-r--r--sb_builder/config.py57
1 files changed, 57 insertions, 0 deletions
diff --git a/sb_builder/config.py b/sb_builder/config.py
new file mode 100644
index 0000000..4549b66
--- /dev/null
+++ b/sb_builder/config.py
@@ -0,0 +1,57 @@
+# sb-builder - GUI configuration
+# 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.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""GUI configuration: built-in defaults overlaid by the user's TOML file."""
+
+import copy
+import os
+import tomllib
+
+DEFAULTS = {
+ "remote": {"target": "buildsystem", "ssh_options": []},
+ "refresh": {"dashboard": 30, "jobs": 5},
+ "notify": {"enabled": True, "events": ["finished", "failed", "killed", "lost", "vm-up"]},
+ "vm": {"libvirt_uri": "qemu:///system", "domain": "buildsystem", "autostart": False},
+}
+
+
+def path():
+ base = os.environ.get("XDG_CONFIG_HOME") or os.path.expanduser("~/.config")
+ return os.path.join(base, "sb-builder", "config.toml")
+
+
+def load(p=None):
+ """Return (config, error). On any problem: the defaults plus the error text."""
+ p = p or path()
+ try:
+ with open(p, "rb") as f:
+ data = tomllib.load(f)
+ except FileNotFoundError:
+ return copy.deepcopy(DEFAULTS), None
+ except (OSError, tomllib.TOMLDecodeError) as e:
+ return copy.deepcopy(DEFAULTS), f"{p}: {e}"
+ cfg = copy.deepcopy(DEFAULTS)
+ for sec, keys in data.items():
+ if sec not in DEFAULTS or not isinstance(keys, dict):
+ return copy.deepcopy(DEFAULTS), f"{p}: unknown section [{sec}]"
+ for k, v in keys.items():
+ if k not in DEFAULTS[sec]:
+ return copy.deepcopy(DEFAULTS), f"{p}: unknown key {sec}.{k}"
+ want = type(DEFAULTS[sec][k])
+ if type(v) is not want:
+ return copy.deepcopy(DEFAULTS), f"{p}: {sec}.{k}: expected {want.__name__}"
+ cfg[sec][k] = v
+ return cfg, None