use core::convert::{TryFrom, TryInto};
use heapless::Vec;
use crate::{utils::*, MqttError, Pid};
use super::{property, PropertyType};
#[derive(Debug, Clone, PartialEq)]
pub struct SubAck<'a> {
pub pid: Pid,
pub reason_codes: Vec<SubscribeReasonCode, 32>,
pub properties: Option<SubAckProperties<'a>>,
}
impl<'a> SubAck<'a> {
pub fn new(pid: Pid, reason_codes: heapless::Vec<SubscribeReasonCode, 32>) -> Self {
SubAck {
pid,
reason_codes,
properties: None,
}
}
pub fn read(buf: &'a [u8], offset: &mut usize) -> Result<Self, MqttError> {
let pid = Pid::read(buf, offset)?;
let properties = SubAckProperties::read(buf, offset)?;
*offset += 1;
let mut reason_codes = heapless::Vec::<SubscribeReasonCode, 32>::new();
for byte in &buf[*offset..] {
let reason_code: SubscribeReasonCode = TryInto::<SubscribeReasonCode>::try_into(*byte)?;
reason_codes
.push(reason_code)
.map_err(|_| MqttError::TooManySubscribeReasonCodes)?;
}
let suback = SubAck {
pid,
reason_codes,
properties,
};
Ok(suback)
}
pub fn write(&self, buf: &mut [u8], offset: &mut usize) -> Result<usize, MqttError> {
check_remaining(buf, offset, self.len() + 1)?;
let header: u8 = 0x90;
write_u8(buf, offset, header);
let write_len = write_length(buf, offset, self.len())? + 1;
self.pid.write(buf, offset);
match &self.properties {
Some(properties) => properties.write(buf, offset)?,
None => write_u8(buf, offset, 0),
}
for reason_code in &self.reason_codes {
write_u8(buf, offset, *reason_code as u8);
}
Ok(write_len)
}
fn len(&self) -> usize {
let mut len = 2 + self.reason_codes.len();
match &self.properties {
Some(properties) => {
let properties_len = properties.len();
let properties_len_len = len_len(properties_len);
len += properties_len_len + properties_len;
}
None => {
len += 1;
}
}
len
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct SubAckProperties<'a> {
pub reason_string: Option<&'a str>,
pub user_properties: Vec<(&'a str, &'a str), 32>,
}
impl<'a> SubAckProperties<'a> {
pub fn read(buf: &'a [u8], offset: &mut usize) -> Result<Option<Self>, MqttError> {
let mut properties = Self::default();
let properties_len = buf[*offset];
if properties_len == 0 {
return Ok(None);
}
let mut cursor: usize = 1;
while cursor < properties_len.into() {
let mut prop_offset = *offset + cursor;
let prop = buf[prop_offset];
prop_offset += 1;
cursor += 1;
match property(prop)? {
PropertyType::ReasonString => {
let reason = read_str(buf, &mut prop_offset)?;
cursor += 2 + reason.len();
properties.reason_string = Some(reason);
}
PropertyType::UserProperty => {
let key = read_str(buf, &mut prop_offset)?;
let value = read_str(buf, &mut prop_offset)?;
cursor += 2 + key.len() + 2 + value.len();
properties
.user_properties
.push((key, value))
.map_err(|_| MqttError::TooManyUserProperties)?;
}
prop => return Err(MqttError::InvalidPropertyType(prop)),
}
}
*offset += properties.len();
Ok(Some(properties))
}
pub fn write(&self, buf: &mut [u8], offset: &mut usize) -> Result<(), MqttError> {
write_length(buf, offset, self.len())?;
if let Some(reason_string) = self.reason_string {
write_u8(buf, offset, PropertyType::ReasonString as u8);
write_bytes_with_len(buf, offset, reason_string.as_bytes());
}
for (key, value) in self.user_properties.iter() {
write_u8(buf, offset, PropertyType::UserProperty as u8);
write_bytes_with_len(buf, offset, key.as_bytes());
write_bytes_with_len(buf, offset, value.as_bytes());
}
Ok(())
}
fn len(&self) -> usize {
let mut len = 0;
if let Some(reason) = &self.reason_string {
len += 1 + 2 + reason.len();
}
for (key, value) in self.user_properties.iter() {
len += 1 + 2 + key.len() + 2 + value.len();
}
len
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum SubscribeReasonCode {
QoS0 = 0x00,
QoS1 = 0x01,
QoS2 = 0x02,
Unspecified = 0x80,
ImplementationSpecific = 0x83,
NotAuthorized = 0x87,
TopicFilterInvalid = 0x8F,
PidInUse = 0x91,
QuotaExceeded = 0x97,
SharedSubscriptionsNotSupported = 0x9E,
SubscriptionIdNotSupported = 0xA1,
WildcardSubscriptionsNotSupported = 0xA2,
}
impl TryFrom<u8> for SubscribeReasonCode {
type Error = MqttError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
let v = match value {
0 => SubscribeReasonCode::QoS0,
1 => SubscribeReasonCode::QoS1,
2 => SubscribeReasonCode::QoS2,
128 => SubscribeReasonCode::Unspecified,
131 => SubscribeReasonCode::ImplementationSpecific,
135 => SubscribeReasonCode::NotAuthorized,
143 => SubscribeReasonCode::TopicFilterInvalid,
145 => SubscribeReasonCode::PidInUse,
151 => SubscribeReasonCode::QuotaExceeded,
158 => SubscribeReasonCode::SharedSubscriptionsNotSupported,
161 => SubscribeReasonCode::SubscriptionIdNotSupported,
162 => SubscribeReasonCode::WildcardSubscriptionsNotSupported,
code => return Err(MqttError::InvalidReasonCode(code)),
};
Ok(v)
}
}
#[cfg(test)]
mod tests {
use crate::Pid;
use super::{SubAck, SubAckProperties, SubscribeReasonCode};
fn sample<'a>() -> SubAck<'a> {
let properties = SubAckProperties {
reason_string: Some("test"),
user_properties: heapless::Vec::<_, 32>::from_slice(&[("test", "test")]).unwrap(),
};
SubAck {
pid: Pid::new(42),
reason_codes: heapless::Vec::<_, 32>::from_slice(&[
SubscribeReasonCode::QoS0,
SubscribeReasonCode::QoS1,
SubscribeReasonCode::QoS2,
SubscribeReasonCode::Unspecified,
])
.unwrap(),
properties: Some(properties),
}
}
fn sample_bytes() -> Vec<u8> {
vec![
0x90, 0x1b, 0x00, 0x2a, 0x14, 0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74,
0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x01, 0x02, 0x80, ]
}
#[test]
fn suback_decode() {
let bytes = &sample_bytes()[2..];
let expected = sample();
let actual = SubAck::read(bytes, &mut 0).unwrap();
assert_eq!(expected, actual);
}
#[test]
fn suback_encode() {
let expected = sample_bytes();
let mut buf = [0u8; 29];
sample().write(&mut buf, &mut 0).unwrap();
assert_eq!(expected.as_slice(), buf);
}
fn sample2<'a>() -> SubAck<'a> {
SubAck {
pid: Pid::new(42),
reason_codes: heapless::Vec::<_, 32>::from_slice(&[
SubscribeReasonCode::QoS0,
SubscribeReasonCode::QoS1,
SubscribeReasonCode::QoS2,
SubscribeReasonCode::Unspecified,
])
.unwrap(),
properties: None,
}
}
fn sample_bytes2() -> Vec<u8> {
vec![
0x90, 0x07, 0x00, 0x2a, 0x00, 0x00, 0x01, 0x02, 0x80, ]
}
#[test]
fn suback_decode2() {
let bytes = &sample_bytes2()[2..];
let expected = sample2();
let actual = SubAck::read(bytes, &mut 0).unwrap();
assert_eq!(expected, actual);
}
#[test]
fn suback_encode2() {
let expected = sample_bytes2();
let mut buf = [0u8; 9];
sample2().write(&mut buf, &mut 0).unwrap();
assert_eq!(expected.as_slice(), buf);
}
}