#![forbid(unsafe_code)]
use crate::error::ImError;
use crate::{expect_message_struct, skip_container};
use matter_codec::{Element, Tag, TlvReader, Value};
pub fn parse_status_response(bytes: &[u8]) -> Result<Option<u8>, ImError> {
let mut r = TlvReader::new(bytes);
expect_message_struct(&mut r)?;
loop {
match r.next()? {
Some(Element::Scalar {
tag: Tag::Context(0),
value: Value::Uint(n),
}) => {
let code = u8::try_from(n).map_err(|_| ImError::InvalidStatusCode { code: n })?;
return Ok(Some(code));
}
None
| Some(
Element::ContainerEnd
| Element::Scalar {
tag: Tag::Context(0),
..
}
| Element::ContainerStart {
tag: Tag::Context(0),
..
},
) => return Ok(None),
Some(Element::ContainerStart { .. }) => skip_container(&mut r)?,
Some(_) => {}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ImStatus {
Success,
Failure(u8),
}
impl ImStatus {
#[must_use]
pub fn from_u8(code: u8) -> Self {
if code == 0x00 {
Self::Success
} else {
Self::Failure(code)
}
}
#[must_use]
pub fn to_u8(self) -> u8 {
match self {
Self::Success => 0x00,
Self::Failure(code) => code,
}
}
#[must_use]
pub fn is_success(self) -> bool {
matches!(self, Self::Success)
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)] use super::*;
#[test]
fn maps_success_and_failure_codes() {
assert_eq!(ImStatus::from_u8(0x00), ImStatus::Success);
assert!(matches!(ImStatus::from_u8(0x01), ImStatus::Failure(0x01)));
assert!(matches!(ImStatus::from_u8(0x88), ImStatus::Failure(0x88)));
}
#[test]
fn is_success_only_for_zero() {
assert!(ImStatus::from_u8(0x00).is_success());
assert!(!ImStatus::from_u8(0x01).is_success());
}
#[test]
fn parse_status_response_reads_bare_status() {
let bytes = crate::build_status_response(0xc6);
assert_eq!(parse_status_response(&bytes).unwrap(), Some(0xc6));
let ok = crate::build_status_response(0x00);
assert_eq!(parse_status_response(&ok).unwrap(), Some(0x00));
}
#[test]
fn parse_status_response_none_for_write_response() {
let write = crate::build_write_request(&[]);
assert_eq!(parse_status_response(&write).unwrap(), None);
}
#[test]
fn parse_status_response_none_for_invoke_request() {
let inv = crate::build_invoke_request(
crate::CommandPath {
endpoint: 1,
cluster: 0x06,
command: 0x02,
},
&[0x15, 0x18],
);
assert_eq!(parse_status_response(&inv).unwrap(), None);
}
}