90 lines
2.8 KiB
Python
90 lines
2.8 KiB
Python
"""Pure reverse index for 1C common-command group membership."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from collections.abc import Iterable, Mapping
|
|
from typing import Any
|
|
|
|
from .payload import decode_payload_lossless, parse_brace_text
|
|
|
|
|
|
_GUID_RE = re.compile(
|
|
r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
|
|
)
|
|
|
|
|
|
def _tree_item_at_path(tree: Any, path: tuple[int, ...]) -> Any | None:
|
|
node = tree
|
|
for index in path:
|
|
if not isinstance(node, dict) or not isinstance(node.get("items"), list):
|
|
return None
|
|
items = node["items"]
|
|
if index < 0 or index >= len(items):
|
|
return None
|
|
node = items[index]
|
|
return node
|
|
|
|
|
|
def _tree_scalar_at_path(tree: Any, path: tuple[int, ...]) -> str:
|
|
node = _tree_item_at_path(tree, path)
|
|
if isinstance(node, dict) and node.get("type") in {"atom", "string"}:
|
|
return str(node.get("value") or "")
|
|
return ""
|
|
|
|
|
|
def common_command_group_guid(tree: Any) -> str | None:
|
|
"""Return the group GUID stored in an observed CommonCommand Config tree."""
|
|
body = _tree_item_at_path(tree, (1, 1, 2))
|
|
group_guid = _tree_scalar_at_path(body, (7, 1)).strip().lower()
|
|
return group_guid if _GUID_RE.fullmatch(group_guid) else None
|
|
|
|
|
|
def parse_common_command_tree(data: bytes) -> Any | None:
|
|
"""Decode a CommonCommand payload without raising on unsupported data."""
|
|
try:
|
|
decoded = decode_payload_lossless(data)
|
|
text = decoded.get("text")
|
|
if not text or "{" not in text:
|
|
return None
|
|
return parse_brace_text(text)
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def index_common_command_groups(
|
|
commands: Iterable[Mapping[str, Any]],
|
|
payloads: Mapping[str, bytes],
|
|
) -> dict[str, Any]:
|
|
"""Build ``CommandGroup GUID -> CommonCommand rows`` from Config payloads."""
|
|
command_rows = [dict(item) for item in commands]
|
|
normalized_payloads = {str(key).strip().lower(): value for key, value in payloads.items()}
|
|
groups: dict[str, list[dict[str, Any]]] = {}
|
|
source_missing = 0
|
|
undecodable = 0
|
|
unassigned = 0
|
|
|
|
for item in command_rows:
|
|
guid = str(item.get("guid") or "").strip().lower()
|
|
data = normalized_payloads.get(guid)
|
|
if not data:
|
|
source_missing += 1
|
|
continue
|
|
tree = parse_common_command_tree(data)
|
|
if tree is None:
|
|
undecodable += 1
|
|
continue
|
|
group_guid = common_command_group_guid(tree)
|
|
if group_guid is None:
|
|
unassigned += 1
|
|
continue
|
|
groups.setdefault(group_guid, []).append(item)
|
|
|
|
return {
|
|
"groups": groups,
|
|
"scanned": len(command_rows),
|
|
"source_missing": source_missing,
|
|
"undecodable": undecodable,
|
|
"unassigned": unassigned,
|
|
}
|