#![forbid(unsafe_code)]
mod accumulator;
pub mod error;
pub mod event;
pub mod invoke;
pub mod invoke_server;
pub mod path;
pub mod read;
pub mod status;
pub mod subscription;
pub mod timed;
pub mod write;
pub use accumulator::{ReportAccumulator, DEFAULT_MAX_BYTES, DEFAULT_MAX_ELEMENTS};
pub use error::ImError;
pub use event::{
EventFilter, EventPath, EventPriority, EventReport, EventReportItem, EventTimestamp,
};
pub use invoke::{
build_invoke_request, build_invoke_request_batch, build_invoke_request_group,
build_invoke_request_timed, parse_invoke_response, parse_invoke_response_batch, InvokeResponse,
InvokeResponseEntry,
};
pub use invoke_server::{
build_invoke_response_command, build_invoke_response_status, parse_invoke_request,
InvokedCommand, ParsedInvokeRequest,
};
pub use path::{AttributePath, CommandPath, ReadPath};
pub use read::{
build_read_request, build_read_request_full, build_read_request_paths, parse_report_data,
AttributeReportItem, ReportData, ReportOp,
};
pub use status::{parse_status_response, ImStatus};
pub use subscription::{
build_status_response, build_subscribe_request, parse_subscribe_response, SubscribeRequest,
SubscribeResponse,
};
pub use timed::build_timed_request;
pub use write::{
build_list_write_chunks, build_write_request, build_write_request_timed, parse_write_response,
AttributeWriteRequest,
};
pub const IM_REVISION: u8 = 11;
use matter_codec::{ContainerKind, Element, Tag, TlvReader, Value};
pub fn expect_message_struct(r: &mut TlvReader<'_>) -> Result<(), error::ImError> {
match r.next()? {
Some(Element::ContainerStart {
tag: Tag::Anonymous,
kind: ContainerKind::Structure,
}) => Ok(()),
Some(_) | None => Err(error::ImError::NotAStruct),
}
}
pub fn read_container_members(r: &mut TlvReader<'_>) -> Result<Vec<(Tag, Value)>, error::ImError> {
let mut out = Vec::new();
loop {
match r.next()? {
None => {
return Err(error::ImError::Codec(
matter_codec::Error::UnclosedContainer,
))
}
Some(Element::ContainerEnd) => return Ok(out),
Some(Element::Scalar { tag, value }) => out.push((tag, value)),
Some(Element::ContainerStart { tag, kind }) => {
let v = read_container_value(r, kind)?;
out.push((tag, v));
}
Some(_) => {}
}
}
}
pub fn read_container_value(
r: &mut TlvReader<'_>,
kind: ContainerKind,
) -> Result<Value, error::ImError> {
if matches!(kind, ContainerKind::Array) {
let mut elements = Vec::new();
loop {
match r.next()? {
None => {
return Err(error::ImError::Codec(
matter_codec::Error::UnclosedContainer,
))
}
Some(Element::ContainerEnd) => return Ok(Value::Array(elements)),
Some(Element::Scalar { value, .. }) => elements.push(value),
Some(Element::ContainerStart {
kind: inner_kind, ..
}) => elements.push(read_container_value(r, inner_kind)?),
Some(_) => {}
}
}
}
let members = read_container_members(r)?;
Ok(match kind {
ContainerKind::Structure => Value::Structure(members),
_ => Value::List(members),
})
}
pub fn skip_container(r: &mut TlvReader<'_>) -> Result<(), error::ImError> {
r.skip_container().map_err(error::ImError::Codec)
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)] use super::*;
use matter_codec::{Tag, TlvWriter};
#[test]
fn skip_container_leaves_reader_at_next_sibling() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.start_structure(Tag::Context(1)).unwrap(); w.put_uint(Tag::Context(0), 7).unwrap();
w.start_array(Tag::Context(1)).unwrap();
w.put_bytes(Tag::Anonymous, &[0xAA; 40]).unwrap();
w.end_container().unwrap();
w.end_container().unwrap(); w.put_uint(Tag::Context(2), 42).unwrap(); w.end_container().unwrap();
let mut r = TlvReader::new(&buf);
assert!(matches!(
r.next().unwrap(),
Some(Element::ContainerStart { .. })
)); assert!(matches!(
r.next().unwrap(),
Some(Element::ContainerStart { .. })
)); skip_container(&mut r).unwrap();
match r.next().unwrap() {
Some(Element::Scalar {
tag: Tag::Context(2),
value: Value::Uint(42),
}) => {}
other => panic!("expected sentinel after skip, got {other:?}"),
}
}
#[test]
fn read_container_value_array_matches_codec_tree_builder_shape() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_array(Tag::Anonymous).unwrap();
w.put_uint(Tag::Anonymous, 1).unwrap();
w.start_array(Tag::Anonymous).unwrap();
w.put_uint(Tag::Anonymous, 2).unwrap();
w.end_container().unwrap();
w.put_utf8(Tag::Anonymous, "x").unwrap();
w.end_container().unwrap();
let mut r = TlvReader::new(&buf);
let Some(Element::ContainerStart { kind, .. }) = r.next().unwrap() else {
panic!("expected array start");
};
let v = read_container_value(&mut r, kind).unwrap();
assert_eq!(
v,
Value::Array(vec![
Value::Uint(1),
Value::Array(vec![Value::Uint(2)]),
Value::Utf8(String::from("x")),
])
);
}
}