Files

80 lines
2.3 KiB
Python

"""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)
]