"""Configuration loading and validation for deck-it.""" import os import re import sys import tomllib from pathlib import Path ROOT = Path(__file__).resolve().parent.parent MONITOR_NAME = re.compile(r"[A-Za-z0-9_-]{1,32}") def user_config_path() -> Path: return Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) / "deck-it" / "config.toml" 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 _" 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" for key in ("command_center", "display"): if key in ports: v = ports[key] if not isinstance(v, int) or not 1024 <= v <= 65535: 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" return None 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) if err := config_error(cfg): sys.exit(f"{path}: {err}") return cfg