use crate::error::ClusterError;
use crate::types::Nullable;
use matter_codec::{ContainerKind, Element, Tag, TlvReader, Value};
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct SemanticTagStruct {
pub mfg_code: Nullable<u16>,
pub namespace_id: u8,
pub tag: u8,
pub label: Option<Nullable<String>>,
}
impl SemanticTagStruct {
pub fn decode_from(r: &mut TlvReader<'_>) -> Result<Self, ClusterError> {
let mut mfg_code: Option<Nullable<u16>> = None;
let mut namespace_id: Option<u8> = None;
let mut tag: Option<u8> = None;
let mut label: Option<Nullable<String>> = None;
loop {
match r.next()? {
Some(Element::ContainerEnd) => break,
Some(Element::Scalar {
tag: Tag::Context(0),
value: Value::Null,
}) => {
mfg_code = Some(Nullable::Null);
}
Some(Element::Scalar {
tag: Tag::Context(0),
value: Value::Uint(v),
}) => {
mfg_code =
Some(Nullable::Value(u16::try_from(v).map_err(|_| {
ClusterError::InvalidLength("SemanticTag.MfgCode")
})?));
}
Some(Element::Scalar {
tag: Tag::Context(1),
value: Value::Uint(v),
}) => {
namespace_id = Some(
u8::try_from(v)
.map_err(|_| ClusterError::InvalidLength("SemanticTag.NamespaceID"))?,
);
}
Some(Element::Scalar {
tag: Tag::Context(2),
value: Value::Uint(v),
}) => {
tag = Some(
u8::try_from(v)
.map_err(|_| ClusterError::InvalidLength("SemanticTag.Tag"))?,
);
}
Some(Element::Scalar {
tag: Tag::Context(3),
value: Value::Null,
}) => {
label = Some(Nullable::Null);
}
Some(Element::Scalar {
tag: Tag::Context(3),
value: Value::Utf8(s),
}) => {
label = Some(Nullable::Value(s));
}
None => return Err(ClusterError::Tlv(matter_codec::Error::UnclosedContainer)),
Some(Element::ContainerStart { .. }) => r.skip_container()?,
Some(_) => {}
}
}
Ok(Self {
mfg_code: mfg_code.ok_or(ClusterError::MissingField("SemanticTag.MfgCode"))?,
namespace_id: namespace_id
.ok_or(ClusterError::MissingField("SemanticTag.NamespaceID"))?,
tag: tag.ok_or(ClusterError::MissingField("SemanticTag.Tag"))?,
label,
})
}
pub fn decode(tlv: &[u8]) -> Result<Self, ClusterError> {
let mut r = TlvReader::new(tlv);
match r.next()? {
Some(Element::ContainerStart {
kind: ContainerKind::Structure,
..
}) => {}
_ => {
return Err(ClusterError::UnexpectedType {
context: "SemanticTagStruct",
})
}
}
Self::decode_from(&mut r)
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::expect_used)]
use super::*;
use matter_codec::TlvWriter;
#[test]
fn decodes_a_minimal_tag() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_null(Tag::Context(0)).unwrap();
w.put_uint(Tag::Context(1), 7).unwrap();
w.put_uint(Tag::Context(2), 3).unwrap();
w.end_container().unwrap();
let t = SemanticTagStruct::decode(&buf).unwrap();
assert_eq!(
t,
SemanticTagStruct {
mfg_code: Nullable::Null,
namespace_id: 7,
tag: 3,
label: None
}
);
}
}