feat(rawcli): TLV/NDEF formatting + multi-line editing (Phase 5)

- tlv.py: parse_tlv() walks a Type-2 TLV block (NULL/LOCK/MEM/NDEF/PROP/
  TERMINATOR, incl. 3-byte 0xFF length); format_tlv() renders an indented
  multi-line breakdown and annotates NDEF messages via the existing
  decode_ndef_annotation. prompt_tlv() opens a multi-line editor to
  compose a TLV as hex across lines.
- app/parser: `tlv <hex>` decodes + prints the breakdown; bare `tlv`
  opens the multi-line editor.

5 new tests (structure, extended length, multi-line format, command).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
michael
2026-07-14 12:15:54 -07:00
parent 9c84f9e7b0
commit 4e7181d289
4 changed files with 149 additions and 1 deletions

View File

@@ -12,6 +12,7 @@ from pm3py.cli.rawcli.parser import parse_token, parse_bytes, parse_line
from pm3py.cli.rawcli.entry import byte_space
from pm3py.cli.rawcli.identify import identify, clear, name_14a
from pm3py.cli.rawcli.catalog import TYPE2, ISO15, catalog_for, catalog_for as _cf
from pm3py.cli.rawcli.tlv import parse_tlv, format_tlv
from pm3py.cli.rawcli.app import dispatch
_ANSI = re.compile(r"\x1b\[[0-9;]*m")
@@ -265,3 +266,33 @@ class TestFunctionCalls:
s = self._id(); dispatch(s, "identify"); capsys.readouterr()
dispatch(s, "help READ")
assert "0x30" in capsys.readouterr().out
class TestTLV:
# NULL + NDEF (a well-known text record "Hi"/en) + TERMINATOR
TEXT_REC = bytes.fromhex("D101055402656E4869") # D1 01 05 T 02 'en' 'Hi'
BLOCK = bytes([0x00, 0x03, len(TEXT_REC)]) + TEXT_REC + bytes([0xFE])
def test_parse_structure(self):
tlvs = parse_tlv(self.BLOCK)
tags = [t.tag for t in tlvs]
assert tags == [0x00, 0x03, 0xFE]
ndef = tlvs[1]
assert ndef.length == len(self.TEXT_REC) and ndef.value == self.TEXT_REC
def test_extended_length(self):
big = bytes([0x03, 0xFF, 0x01, 0x00]) + b"\x00" * 256 + bytes([0xFE])
tlvs = parse_tlv(big)
assert tlvs[0].tag == 0x03 and tlvs[0].length == 256
def test_format_multiline(self):
out = format_tlv(self.BLOCK)
assert "NULL" in out
assert "NDEF MESSAGE len=9" in out
assert "TERMINATOR" in out
assert "\n" in out # genuinely multi-line
assert "" in out # NDEF annotation line present
def test_tlv_command(self, capsys):
dispatch(RawSession(), "tlv " + self.BLOCK.hex())
assert "NDEF MESSAGE" in capsys.readouterr().out