"""Mechanical child-record detection for 1C Config section containers.""" from __future__ import annotations from dataclasses import asdict, dataclass from typing import Any from .payload import GUID_RE, scalar @dataclass(frozen=True) class ChildRecord: index: int path: str node: Any evidence: dict[str, set[str]] def to_dict(self) -> dict[str, Any]: data = asdict(self) data.pop("node", None) data["evidence"] = {key: sorted(value) for key, value in self.evidence.items()} return data def children(node: Any) -> list[Any]: if isinstance(node, dict) and node.get("type") in {"list", "sequence"}: return node.get("items") or [] return [] def collect_evidence(node: Any) -> dict[str, set[str]]: strings: set[str] = set() guids: set[str] = set() def walk(value: Any) -> None: if isinstance(value, dict) and value.get("type") in {"atom", "string"}: text = scalar(value) if not text: return if value.get("type") == "string": strings.add(text) if GUID_RE.fullmatch(text): guids.add(text.lower()) return for child in children(value): walk(child) walk(node) return {"strings": strings, "guids": guids} def declared_child_records(section: Any, section_path: str, *, include_evidence: bool = True) -> list[ChildRecord]: """Return records for the common `{marker, count, record...}` container. The function is deliberately structural. It does not assume that records are attributes, dimensions, enum values, or any other metadata category. """ items = children(section) if len(items) < 2: return [] try: declared_count = int(scalar(items[1])) except ValueError: return [] if declared_count < 0: return [] candidates = items[2 : 2 + declared_count] if len(candidates) != declared_count: return [] return [ ChildRecord( index=index, path=f"{section_path}.{index + 2}", node=record, evidence=collect_evidence(record) if include_evidence else {"strings": set(), "guids": set()}, ) for index, record in enumerate(candidates) ]