Initial SQL-only 1C adapter baseline
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
"""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)
|
||||
]
|
||||
Reference in New Issue
Block a user