aboutsummaryrefslogtreecommitdiffstats
path: root/deckit/config.py
diff options
context:
space:
mode:
Diffstat (limited to 'deckit/config.py')
-rw-r--r--deckit/config.py32
1 files changed, 29 insertions, 3 deletions
diff --git a/deckit/config.py b/deckit/config.py
index aac5ad9..e20bc00 100644
--- a/deckit/config.py
+++ b/deckit/config.py
@@ -24,6 +24,8 @@ from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
MONITOR_NAME = re.compile(r"[A-Za-z0-9_-]{1,32}")
+WIFI_ADDR = re.compile(r"[A-Za-z0-9.-]{1,253}:[0-9]{1,5}")
+DESKTOP_ID = re.compile(r"[A-Za-z0-9._-]{1,255}")
def user_config_path() -> Path:
@@ -34,6 +36,8 @@ def config_error(cfg: dict) -> str | None:
d, ports = cfg.get("display", {}), cfg.get("ports", {})
if not MONITOR_NAME.fullmatch(str(d.get("name", "TAB"))):
return "display.name must be letters, digits, - or _"
+ if not isinstance(d.get("enabled", True), bool):
+ return "display.enabled must be true or false"
refresh = d.get("refresh", 60)
if not isinstance(refresh, int) or not 1 <= refresh <= 240:
return "display.refresh must be a number from 1 to 240"
@@ -44,13 +48,35 @@ def config_error(cfg: dict) -> str | None:
return f"ports.{key} must be a number from 1024 to 65535"
if ports.get("command_center", 8765) == ports.get("display", 5900):
return "ports.command_center and ports.display must differ"
+ addr = cfg.get("wifi", {}).get("address", "")
+ if addr and not (isinstance(addr, str) and WIFI_ADDR.fullmatch(addr)):
+ return "wifi.address must be host:port, like 192.0.2.10:37099"
+ apps = cfg.get("launch", {}).get("apps", [])
+ if not isinstance(apps, list) or not all(isinstance(a, str) and DESKTOP_ID.fullmatch(a) for a in apps):
+ return "launch.apps must be a list of desktop ids, like \"kitty\""
return None
+def merge(base: dict, over: dict) -> dict:
+ """The user's file over the defaults, key by key within each table."""
+ out = dict(base)
+ for key, value in over.items():
+ both = isinstance(value, dict) and isinstance(base.get(key), dict)
+ out[key] = {**base[key], **value} if both else value
+ return out
+
+
+def defaults() -> dict:
+ with open(ROOT / "config.toml", "rb") as f:
+ return tomllib.load(f)
+
+
def load_config() -> dict:
- path = user_config_path() if user_config_path().is_file() else ROOT / "config.toml"
- with open(path, "rb") as f:
- cfg = tomllib.load(f)
+ cfg, path = defaults(), ROOT / "config.toml"
+ if user_config_path().is_file():
+ path = user_config_path()
+ with open(path, "rb") as f:
+ cfg = merge(cfg, tomllib.load(f))
if err := config_error(cfg):
sys.exit(f"{path}: {err}")
return cfg