"""Validation and normalization helpers for run configuration payloads.""" from __future__ import annotations from typing import Any from python_app.models.run_config_schema import ComboModel, RingEndpointModel, SwitchModel def as_dict(value: Any, context: str) -> dict[str, Any]: """Validate that a payload node is a JSON object and return it.""" if value is None: return {} if not isinstance(value, dict): raise ValueError(f"{context} must be a JSON object") return value def load_switch_payload( payload: dict[str, Any], target: SwitchModel, ) -> None: """Populate switch model from payload preserving defaults for missing values.""" target.name = str(payload.get("name", target.name)) target.driver_mode = str(payload.get("driver_mode", target.driver_mode)) target.driver = str(payload.get("driver", target.driver)) target.radar_port = int(payload.get("radar_port", target.radar_port)) target.positions = int(payload.get("positions", target.positions)) target.default_position = int(payload.get("default_position", target.default_position)) target.gpio_chip = str(payload.get("gpio_chip", target.gpio_chip)) target.pin_a = int(payload.get("pin_a", target.pin_a)) target.pin_b = int(payload.get("pin_b", target.pin_b)) target.invert_logic = bool(payload.get("invert_logic", target.invert_logic)) def load_ring_payload(payload: dict[str, Any], target: RingEndpointModel) -> None: """Populate ring endpoint model from payload preserving defaults.""" target.name = str(payload.get("name", target.name)) target.capacity = int(payload.get("capacity", target.capacity)) target.slot_size_bytes = int(payload.get("slot_size_bytes", target.slot_size_bytes)) def parse_combos_from_text(text: str) -> list[ComboModel]: """Parse UI combos string in `input:output,input:output` format.""" cleaned = text.strip() if not cleaned: return [] combos: list[ComboModel] = [] for item in cleaned.split(","): pair = item.strip() if not pair: continue if ":" not in pair: raise ValueError(f"Invalid combo syntax: {pair!r}. Expected input:output") input_text, output_text = pair.split(":", 1) combos.append(ComboModel(input=int(input_text.strip()), output=int(output_text.strip()))) if not combos: raise ValueError("No valid combos were provided") return combos