Skip to main content

Module scripts_codec

Module scripts_codec 

Source
Expand description

The Ignition Flint script codec (07-04, INTR-01) — decode/encode of the scripts EMBEDDED inside JSON resource members (Perspective view.json component scripts, tag event scripts, …) into editable .py sidecars, with a byte-exact unedited round-trip.

PURE — the client/resources.rs discipline: zero crate::client::GatewayApi surface, unit-testable without a gateway. ignition/script-python members are ALREADY plain .py text in the export (live-proven, 07-RESEARCH) and never decode — this module targets the escaped string values under SCRIPT_KEYS only (scope honesty, README “Script decode/ encode”).

§The codec (ignition-nvim’s exact contract, dual-ported there)

  • flint_encode — the ordered multi-pass replacement table, BACKSLASH FIRST.
  • flint_decode — a SINGLE-PASS state machine (multi-pass cannot distinguish \\t from \t); unknown \uXXXX escapes keep the backslash.
  • The invariant flint_encode(flint_decode(x)) == x is SACRED (over strings in the table’s image — what Ignition writes).
  • dedent/reindent strip/restore the common leading-TAB prefix (only non-empty lines reindent; whitespace-only lines normalize to empty — scripts with such lines re-encode with that one normalization, an accepted ignition-nvim semantic).

§Addressing = counter-named sidecars + JSON-pointer manifest

--decode-scripts writes the export’s members PLUS <member>.<n>.py sidecar siblings PLUS a scripts-manifest.json at the tree root mapping each member’s JSON-pointer addresses → {sidecar, indent_prefix}. The exported JSON stays MARKER-FREE (gateway-clean — the manifest-aside beats markers, 07-RESEARCH anti-patterns).

§Round-trip = raw byte-span splicing (NO preserve_order)

decode_member/encode_member walk the member’s RAW bytes with ONE shared position-tracking scanner: decode resolves each script string to its byte span, encode RE-RESOLVES each manifest pointer in the CURRENT bytes (hand-edits stay valid) and splices the re-encoded replacement at that span. serde_json’s preserve_order feature is deliberately NOT enabled — feature unification is workspace-wide and would flip every serde_json::Value map to insertion order, churning key order in the existing Value-re-serializing goldens (tags export -o -, doctor/webdev passthroughs). serde_json is used READ-ONLY here (manifest parse/serialize; parse-to-Value walks are order-agnostic); NO code path re-serializes a member Value. Acceptance = byte-equality of UNEDITED re-encoded members (the sacred invariant at file level).

Structs§

DecodedEntry
One entry with its sidecar content.
DecodedMember
One decoded member: the manifest entries plus each sidecar’s text (decoded + dedented) ready to write.
Manifest
The decoded-export manifest carried at the tree root — consumed + stripped by encode_export_tree (it never rides an upload).
ManifestEntry
One manifest entry: the JSON-pointer address of a script string value, its counter-named sidecar sibling (<member>.<n>.py), and the indent prefix dedent stripped.

Constants§

MANIFEST_NAME
The manifest file a decoded export tree carries at its root — consumed + stripped on re-encode (it never enters an uploaded zip).
SCRIPT_KEYS
The JSON keys whose string values carry embedded scripts — the ignition-nvim list verbatim (nine keys; the plan sketch said ten, the dual-ported source is the authority); kept in sync by comment reference (lua/ignition/json_parser.lua SCRIPT_KEYS / ignition_lsp/json_scanner.py SCRIPT_KEYS).

Functions§

count_file_members
Count a zip’s FILE members (directory entries excluded) — the export-decode result’s member count.
decode_export_tree
Decode an export zip into a DIRECTORY: every member written at its path, <member>.<n>.py sidecars beside the .json members that carry embedded scripts, and MANIFEST_NAME at the tree root. Returns the sidecar count. The exported JSON stays MARKER-FREE (gateway-clean).
decode_member
Decode one member’s embedded scripts: walk the RAW bytes for SCRIPT_KEYS string values that [looks_like_script] accepts, producing sidecar texts (decoded + dedented) and manifest entries with counter-named sidecars (<member-basename>.<n>.py). None when the member holds no decodable scripts (or does not scan — the caller leaves such members untouched, byte-verbatim).
dedent
Strip the common leading-TAB prefix Ignition stores scripts with — the ignition-nvim semantics verbatim: the minimum leading-tab count over non-empty lines decides the prefix; stray spaces in the leading whitespace strip alongside the tabs. Returns (dedented_text, indent_prefix) so reindent can restore it.
encode_export_tree
Re-zip a decoded export DIRECTORY back into importable zip bytes: the manifest is consumed + stripped, every sidecar referenced by it is stripped, members with manifest entries ride encode_member (span-level splice), everything else copies verbatim. Missing sidecars keep the member’s current value (the decode rule). A directory without MANIFEST_NAME is not a decoded export tree (usage-class refusal).
encode_member
Encode one member’s scripts back: re-resolve each manifest pointer to its raw byte span in the CURRENT member bytes (the same shared scanner — re-scanning at encode time keeps splices valid even when the user hand-edited the member JSON), then splice the re-encoded replacement (reindent + flint_encode) at that span. Rules:
flint_decode
Decode the Ignition Flint JSON-string form back to plain text — SINGLE-PASS (multi-pass cannot distinguish \\t — literal backslash + t — from \t — a tab). Unknown \uXXXX and unknown single escapes KEEP the backslash (the ignition-nvim semantics: the escape sequence rides through verbatim for the re-encode).
flint_encode
Encode plain text into the Ignition Flint JSON-string form — the EXACT ordered multi-pass table (backslash FIRST so later passes cannot double-escape), cross-validated Lua + Python in ignition-nvim.
reindent
Restore the prefix dedent stripped — ONLY non-empty lines reindent (the ignition-nvim semantics verbatim).