matter-codec 0.2.0

Matter protocol TLV (Tag-Length-Value) encoder and decoder.
Documentation
//! Composite golden vector ported from connectedhomeip's canonical
//! `Encoding1` (`src/lib/core/tests/TestTLV.cpp:293`) — the single densest TLV
//! message chip pins: fully-qualified 6-byte and implicit-profile tags, nested
//! structures + arrays, every signed/unsigned int width, a single-precision
//! float and a double, and a long UTF-8 string, all in one buffer.
//!
//! Our per-element byte-parity is already covered; this adds the *composite*
//! case (all tag forms + nesting in one message) against chip's real bytes.
//!
//! We assert (a) chip's buffer decodes cleanly into a top-level structure, and
//! (b) decode→encode reaches a byte fixed point. We do NOT assert byte-equality
//! against the original buffer: chip encodes some integers non-minimally, and
//! our writer always chooses the minimal width, so the re-encode is shorter
//! (the RE-ENC observation). The fixed-point check is NaN-safe (float bit
//! patterns survive decode→encode) where a `Value` equality would not be.

#![allow(clippy::unwrap_used, clippy::expect_used)] // Test code: CLAUDE.md carve-out.

use matter_codec::{TlvReader, TlvWriter, Value};

/// chip `Encoding1` (TestTLV.cpp), verbatim.
#[rustfmt::skip]
const CHIP_ENCODING1: &[u8] = &[
    0xD5, 0xBB, 0xAA, 0xDD, 0xCC, 0x01, 0x00, 0xC9, 0xBB, 0xAA, 0xDD, 0xCC, 0x02, 0x00, 0x88, 0x02,
    0x00, 0x36, 0x00, 0x00, 0x2A, 0x00, 0xEF, 0x02, 0xF0, 0x67, 0xFD, 0xFF, 0x07, 0x00, 0x90, 0x2F,
    0x50, 0x09, 0x00, 0x00, 0x00, 0x15, 0x18, 0x17, 0xD4, 0xBB, 0xAA, 0xDD, 0xCC, 0x11, 0x00, 0xB4,
    0xA0, 0xBB, 0x0D, 0x00, 0x14, 0xB5, 0x00, 0x28, 0x6B, 0xEE, 0x6D, 0x70, 0x11, 0x01, 0x00, 0x0E,
    0x01, 0x53, 0x54, 0x41, 0x52, 0x54, 0x2E, 0x2E, 0x2E, 0x21, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
    0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x40, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
    0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x23, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
    0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x24, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
    0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x25, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
    0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x5E, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
    0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x26, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
    0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x2A, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
    0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
    0x37, 0x28, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
    0x37, 0x29, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
    0x37, 0x2D, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
    0x37, 0x3D, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
    0x37, 0x5B, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
    0x37, 0x5D, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
    0x37, 0x3B, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
    0x37, 0x27, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x2E, 0x2E, 0x2E, 0x45, 0x4E, 0x44, 0x18,
    0x18, 0x18, 0xCC, 0xBB, 0xAA, 0xDD, 0xCC, 0x05, 0x00, 0x0E, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69,
    0x73, 0x20, 0x61, 0x20, 0x74, 0x65, 0x73, 0x74, 0x8A, 0xFF, 0xFF, 0x33, 0x33, 0x8F, 0x41, 0xAB,
    0x00, 0x00, 0x01, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0xE6, 0x31, 0x40, 0x18,
];

fn decode(bytes: &[u8]) -> (matter_codec::Tag, Value) {
    TlvReader::new(bytes)
        .read_value()
        .expect("chip Encoding1 must decode")
}

fn encode(tag: matter_codec::Tag, value: &Value) -> Vec<u8> {
    let mut buf = Vec::new();
    TlvWriter::new(&mut buf)
        .write_value(tag, value)
        .expect("re-encode");
    buf
}

#[test]
fn chip_encoding1_decodes_as_a_structure() {
    let (_, value) = decode(CHIP_ENCODING1);
    assert!(
        matches!(value, Value::Structure(_)),
        "Encoding1's top level is a fully-qualified structure"
    );
}

#[test]
fn chip_encoding1_decode_encode_is_a_byte_fixed_point() {
    // Decode chip's composite, re-encode it (minimal widths), then decode +
    // re-encode again: the two re-encodings must be byte-identical. This proves
    // our codec is self-consistent on every element type + tag form chip packs
    // into Encoding1, without depending on chip's non-minimal int widths.
    let (tag1, value1) = decode(CHIP_ENCODING1);
    let buf1 = encode(tag1, &value1);
    let (tag2, value2) = decode(&buf1);
    let buf2 = encode(tag2, &value2);
    assert_eq!(buf1, buf2, "decode->encode must reach a byte fixed point");
}

fn contains_string(v: &Value, needle: &str) -> bool {
    match v {
        Value::Utf8(s) => s == needle,
        Value::Structure(members) | Value::List(members) => {
            members.iter().any(|(_, mv)| contains_string(mv, needle))
        }
        Value::Array(items) => items.iter().any(|iv| contains_string(iv, needle)),
        _ => false,
    }
}

#[test]
fn chip_encoding1_contains_the_expected_utf8_string() {
    // Spot-check one leaf survives the composite decode: the "This is a test"
    // char string (a member of the top-level structure).
    let (_, value) = decode(CHIP_ENCODING1);
    assert!(
        contains_string(&value, "This is a test"),
        "the composite must decode its UTF-8 leaf"
    );
}