use core::convert::TryFrom;
use heapless::Vec;
use crate::{protocol::Protocol, utils::*, MqttError, QoS};
use super::{property, PropertyType};
#[derive(Debug, Clone, PartialEq)]
pub struct Connect<'a> {
pub protocol: Protocol,
pub clean_start: bool,
pub keep_alive: u16,
pub client_id: &'a str,
pub last_will: Option<LastWill<'a>>,
pub login: Option<Login<'a>>,
pub properties: Option<ConnectProperties<'a>>,
}
impl<'a> Connect<'a> {
pub fn new(id: &'a str) -> Self {
Connect {
protocol: Protocol::V5,
keep_alive: 10,
properties: None,
client_id: id,
clean_start: true,
last_will: None,
login: None,
}
}
pub fn read(buf: &'a [u8], offset: &mut usize) -> Result<Self, MqttError> {
let protocol = Protocol::read(buf, offset)?;
let connect_flags = buf[*offset];
if (connect_flags & 0x01) != 0 {
return Err(MqttError::MalformedPacket);
}
let clean_start = (connect_flags & 0b10) != 0;
*offset += 1;
let keep_alive = read_u16(buf, *offset);
let properties = match protocol {
Protocol::V5 => {
*offset += 2;
let props = ConnectProperties::read(buf, offset)?;
*offset += 1;
props
}
Protocol::V4 => None,
};
let client_id = read_str(buf, offset)?;
let last_will = LastWill::read(connect_flags, buf, offset)?;
let login = Login::read(connect_flags, buf, offset)?;
let connect = Connect {
protocol,
clean_start,
keep_alive,
client_id,
last_will,
login,
properties,
};
Ok(connect)
}
pub fn write(&self, buf: &mut [u8], offset: &mut usize) -> Result<usize, MqttError> {
let header: u8 = 0x10;
let mut connect_flags = 0b0000_0000;
if self.clean_start {
connect_flags |= 0b10;
};
if let Some(last_will) = &self.last_will {
connect_flags |= 0b0000_0100;
connect_flags |= (last_will.qos as u8) << 3;
if last_will.retain {
connect_flags |= 0b0010_0000;
}
}
if self.login.is_some() {
connect_flags |= 0b1000_0000;
connect_flags |= 0b0100_0000;
}
check_remaining(buf, offset, self.len() + 1)?;
write_u8(buf, offset, header);
let write_len = write_length(buf, offset, self.len())? + 1;
self.protocol.write(buf, offset);
write_u8(buf, offset, connect_flags);
write_u16(buf, offset, self.keep_alive);
match &self.properties {
Some(properties) => properties.write(buf, offset)?,
None => write_u8(buf, offset, 0),
}
write_bytes_with_len(buf, offset, self.client_id.as_bytes());
if let Some(last_will) = &self.last_will {
last_will.write(buf, offset)?;
};
if let Some(login) = &self.login {
login.write(buf, offset);
};
Ok(write_len)
}
fn len(&self) -> usize {
let mut len = 2 + "MQTT".len() + 1 + 1 + 2;
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 += 2 + self.client_id.len();
if let Some(last_will) = &self.last_will {
len += last_will.len();
}
if let Some(login) = &self.login {
len += login.len();
}
len
}
pub fn set_login(&mut self, username: &'a str, password: &'a [u8]) -> &mut Connect<'a> {
let login = Login { username, password };
self.login = Some(login);
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct LastWill<'a> {
pub topic: &'a str,
pub message: &'a [u8],
pub qos: QoS,
pub retain: bool,
pub properties: Option<WillProperties<'a>>,
}
impl<'a> LastWill<'a> {
pub fn new(topic: &'a str, payload: &'a [u8], qos: QoS, retain: bool) -> Self {
LastWill {
topic,
message: payload,
qos,
retain,
properties: None,
}
}
pub fn read(
connect_flags: u8,
buf: &'a [u8],
offset: &mut usize,
) -> Result<Option<Self>, MqttError> {
let last_will = match connect_flags & 0b100 {
0 if (connect_flags & 0b0011_1000) != 0 => {
return Err(MqttError::IncorrectPacketFormat);
}
0 => None,
_ => {
let properties = WillProperties::read(buf, offset)?;
*offset += 1;
let will_topic = read_str(buf, offset)?;
let will_message = read_bytes(buf, offset)?;
let will_qos = QoS::try_from((connect_flags & 0b11000) >> 3)?;
Some(LastWill {
topic: will_topic,
message: will_message,
qos: will_qos,
retain: (connect_flags & 0b0010_0000) != 0,
properties,
})
}
};
Ok(last_will)
}
pub fn write(&self, buf: &mut [u8], offset: &mut usize) -> Result<(), MqttError> {
if let Some(properties) = &self.properties {
properties.write(buf, offset)?;
}
write_bytes_with_len(buf, offset, self.topic.as_bytes());
write_bytes_with_len(buf, offset, self.message);
Ok(())
}
fn len(&self) -> usize {
let mut len = 0;
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 += 2 + self.topic.len() + 2 + self.message.len();
len
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct WillProperties<'a> {
pub payload_format_indicator: Option<u8>,
pub message_expiry_interval: Option<u32>,
pub content_type: Option<&'a str>,
pub response_topic: Option<&'a str>,
pub correlation_data: Option<&'a [u8]>,
pub delay_interval: Option<u32>,
pub user_properties: Vec<(&'a str, &'a str), 32>,
}
impl<'a> WillProperties<'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::WillDelayInterval => {
properties.delay_interval = Some(read_u32(buf, prop_offset));
cursor += 4;
}
PropertyType::PayloadFormatIndicator => {
properties.payload_format_indicator = Some(buf[prop_offset]);
cursor += 1;
}
PropertyType::MessageExpiryInterval => {
properties.message_expiry_interval = Some(read_u32(buf, prop_offset));
cursor += 4;
}
PropertyType::ContentType => {
let typ = read_str(buf, &mut prop_offset)?;
cursor += 2 + typ.len();
properties.content_type = Some(typ);
}
PropertyType::ResponseTopic => {
let topic = read_str(buf, &mut prop_offset)?;
cursor += 2 + topic.len();
properties.response_topic = Some(topic)
}
PropertyType::CorrelationData => {
let data = read_bytes(buf, &mut prop_offset)?;
cursor += 2 + data.len();
properties.correlation_data = Some(data);
}
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(payload) = self.payload_format_indicator {
write_u8(buf, offset, PropertyType::PayloadFormatIndicator as u8);
write_u8(buf, offset, payload);
}
if let Some(expiry_interval) = self.message_expiry_interval {
write_u8(buf, offset, PropertyType::MessageExpiryInterval as u8);
write_bytes(buf, offset, &expiry_interval.to_be_bytes());
}
if let Some(content_type) = self.content_type {
write_u8(buf, offset, PropertyType::ContentType as u8);
write_bytes_with_len(buf, offset, content_type.as_bytes());
}
if let Some(response_topic) = self.response_topic {
write_u8(buf, offset, PropertyType::ResponseTopic as u8);
write_bytes_with_len(buf, offset, response_topic.as_bytes());
}
if let Some(correlation_data) = self.correlation_data {
write_u8(buf, offset, PropertyType::CorrelationData as u8);
write_bytes_with_len(buf, offset, correlation_data);
}
if let Some(delay_interval) = self.delay_interval {
write_u8(buf, offset, PropertyType::WillDelayInterval as u8);
write_bytes(buf, offset, &delay_interval.to_be_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 self.delay_interval.is_some() {
len += 1 + 4;
}
if self.payload_format_indicator.is_some() {
len += 1 + 1;
}
if self.message_expiry_interval.is_some() {
len += 1 + 4;
}
if let Some(typ) = &self.content_type {
len += 1 + 2 + typ.len()
}
if let Some(topic) = &self.response_topic {
len += 1 + 2 + topic.len()
}
if let Some(data) = &self.correlation_data {
len += 1 + 2 + data.len()
}
for (key, value) in self.user_properties.iter() {
len += 1 + 2 + key.len() + 2 + value.len();
}
len
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Login<'a> {
pub username: &'a str,
pub password: &'a [u8],
}
impl<'a> Login<'a> {
pub fn write(&self, buf: &mut [u8], offset: &mut usize) {
write_bytes_with_len(buf, offset, self.username.as_bytes());
write_bytes_with_len(buf, offset, self.password);
}
pub fn read(
connect_flags: u8,
buf: &'a [u8],
offset: &mut usize,
) -> Result<Option<Self>, MqttError> {
let username = match connect_flags & 0b1000_0000 {
0 => return Ok(None),
_ => read_str(buf, offset)?,
};
let password = match connect_flags & 0b0100_0000 {
0 => return Ok(None),
_ => read_bytes(buf, offset)?,
};
if username.is_empty() && password.is_empty() {
Ok(None)
} else {
Ok(Some(Login { username, password }))
}
}
fn len(&self) -> usize {
let mut len = 0;
if !self.username.is_empty() {
len += 2 + self.username.len();
}
if !self.password.is_empty() {
len += 2 + self.password.len();
}
len
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct ConnectProperties<'a> {
pub session_expiry_interval: Option<u32>,
pub receive_maximum: Option<u16>,
pub max_packet_size: Option<u32>,
pub topic_alias_max: Option<u16>,
pub request_response_info: Option<u8>,
pub request_problem_info: Option<u8>,
pub user_properties: Vec<(&'a str, &'a str), 32>,
pub authentication_method: Option<&'a str>,
pub authentication_data: Option<&'a [u8]>,
}
impl<'a> ConnectProperties<'a> {
pub fn write(&self, buf: &mut [u8], offset: &mut usize) -> Result<(), MqttError> {
write_length(buf, offset, self.len())?;
if let Some(session_expiry_interval) = self.session_expiry_interval {
write_u8(buf, offset, PropertyType::SessionExpiryInterval as u8);
write_bytes(buf, offset, &session_expiry_interval.to_be_bytes());
}
if let Some(receive_maximum) = self.receive_maximum {
write_u8(buf, offset, PropertyType::ReceiveMaximum as u8);
write_u16(buf, offset, receive_maximum);
}
if let Some(max_packet_size) = self.max_packet_size {
write_u8(buf, offset, PropertyType::MaximumPacketSize as u8);
write_bytes(buf, offset, &max_packet_size.to_be_bytes());
}
if let Some(topic_alias_max) = self.topic_alias_max {
write_u8(buf, offset, PropertyType::TopicAliasMaximum as u8);
write_u16(buf, offset, topic_alias_max);
}
if let Some(request_response_info) = self.request_response_info {
write_u8(buf, offset, PropertyType::RequestResponseInformation as u8);
write_u8(buf, offset, request_response_info);
}
if let Some(request_problem_info) = self.request_problem_info {
write_u8(buf, offset, PropertyType::RequestProblemInformation as u8);
write_u8(buf, offset, request_problem_info);
}
if let Some(authentication_method) = self.authentication_method {
write_u8(buf, offset, PropertyType::AuthenticationMethod as u8);
write_bytes_with_len(buf, offset, authentication_method.as_bytes());
}
if let Some(authentication_data) = self.authentication_data {
write_u8(buf, offset, PropertyType::AuthenticationData as u8);
write_bytes_with_len(buf, offset, authentication_data);
}
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(())
}
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::SessionExpiryInterval => {
properties.session_expiry_interval = Some(read_u32(buf, prop_offset));
cursor += 4;
}
PropertyType::ReceiveMaximum => {
properties.receive_maximum = Some(read_u16(buf, prop_offset));
cursor += 2;
}
PropertyType::MaximumPacketSize => {
properties.max_packet_size = Some(read_u32(buf, prop_offset));
cursor += 4;
}
PropertyType::TopicAliasMaximum => {
properties.topic_alias_max = Some(read_u16(buf, prop_offset));
cursor += 2;
}
PropertyType::RequestResponseInformation => {
properties.request_response_info = Some(buf[prop_offset]);
cursor += 1;
}
PropertyType::RequestProblemInformation => {
properties.request_problem_info = Some(buf[prop_offset]);
cursor += 1;
}
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)?;
}
PropertyType::AuthenticationMethod => {
let method = read_str(buf, &mut prop_offset)?;
cursor += 2 + method.len();
properties.authentication_method = Some(method);
}
PropertyType::AuthenticationData => {
let data = read_bytes(buf, &mut prop_offset)?;
cursor += 2 + data.len();
properties.authentication_data = Some(data);
}
prop => return Err(MqttError::InvalidPropertyType(prop)),
}
}
*offset += properties.len();
Ok(Some(properties))
}
fn len(&self) -> usize {
let mut len = 0;
if self.session_expiry_interval.is_some() {
len += 1 + 4;
}
if self.receive_maximum.is_some() {
len += 1 + 2;
}
if self.max_packet_size.is_some() {
len += 1 + 4;
}
if self.topic_alias_max.is_some() {
len += 1 + 2;
}
if self.request_response_info.is_some() {
len += 1 + 1;
}
if self.request_problem_info.is_some() {
len += 1 + 1;
}
for (key, value) in self.user_properties.iter() {
len += 1 + 2 + key.len() + 2 + value.len();
}
if let Some(authentication_method) = &self.authentication_method {
len += 1 + 2 + authentication_method.len();
}
if let Some(authentication_data) = &self.authentication_data {
len += 1 + 2 + authentication_data.len();
}
len
}
}
#[cfg(test)]
mod tests {
use super::{Connect, ConnectProperties, LastWill, Login, WillProperties};
use crate::{protocol::Protocol, QoS};
fn sample_bytes() -> Vec<u8> {
vec![
0x10, 0x1a, 0x00, 0x04, 0x4d, 0x51, 0x54, 0x54, 0x05, 0xc2, 0x00, 0x3c, 0x19, 0x11, 0x00, 0x00, 0x00, 0x78, 0x21, 0x01, 0x00, 0x27, 0x00, 0x00, 0x01, 0x09, 0x16, 0x00, 0x09, 0x61, 0x75, 0x74, 0x68, 0x2d, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x02, 0x69, 0x7a, 0x00, 0x04, 0x31, 0x32, 0x33, 0x34, ]
}
#[rustfmt::skip::macros(vec)]
fn sample_bytes_with_will() -> Vec<u8> {
vec![
0x10, 0x71, 0x00, 0x04, 0x4d, 0x51, 0x54, 0x54, 0x05, 0xf4, 0x00, 0x3c, 0x03, 0x21, 0x00, 0x14, 0x00, 0x07, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x3f,
0x01, 0x01,
0x02, 0x00, 0x00, 0x00, 0x78,
0x03, 0x00, 0x04, 0x74, 0x65, 0x78, 0x74,
0x08, 0x00, 0x0c, 0x77, 0x69, 0x6c, 0x6c, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64,
0x09, 0x00, 0x04, 0x31, 0x32, 0x33, 0x34,
0x18, 0x00, 0x00, 0x00, 0x05,
0x26, 0x00, 0x09, 0x73, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x08, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x04, 0x77, 0x69, 0x6c, 0x6c, 0x00, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x77, 0x69, 0x6c, 0x6c, 0x00, 0x02, 0x69, 0x7a, 0x00, 0x04, 0x31, 0x32, 0x33, 0x34, ]
}
fn sample_connect_properties<'a>() -> ConnectProperties<'a> {
let props = ConnectProperties {
authentication_data: Some("auth-data".as_bytes()),
max_packet_size: Some(265),
receive_maximum: Some(256),
session_expiry_interval: Some(120),
..Default::default()
};
props
}
fn sample_last_will<'a>() -> LastWill<'a> {
LastWill {
topic: "will",
message: "lastwill".as_bytes(),
qos: QoS::ExactlyOnce,
retain: true,
properties: Some(sample_will_properties()),
}
}
fn sample_will_properties<'a>() -> WillProperties<'a> {
let props = WillProperties {
delay_interval: Some(5),
payload_format_indicator: Some(1),
message_expiry_interval: Some(120),
content_type: Some("text"),
response_topic: Some("will_respond"),
correlation_data: Some("1234".as_bytes()),
user_properties: heapless::Vec::<_, 32>::from_slice(&[("sfilename", "test.txt")])
.unwrap(),
};
props
}
fn sample_login<'a>() -> Login<'a> {
Login {
username: "iz",
password: "1234".as_bytes(),
}
}
#[test]
fn connect_properties_write() {
let expected = &sample_bytes()[12..38];
let props = sample_connect_properties();
let mut buf = [0u8; 26];
let mut offset = 0;
props.write(&mut buf, &mut offset).unwrap();
assert_eq!(buf, expected);
}
#[test]
fn login_read() {
let packet = sample_bytes();
let connect_flags = packet[9];
let login = Login::read(connect_flags, &packet, &mut 40)
.unwrap()
.unwrap();
let username = "iz";
let password: &[u8] = &[b'1', b'2', b'3', b'4'];
assert_eq!(login, Login { username, password });
}
#[test]
fn login_props_len() {
let packet = sample_bytes();
let connect_flags = packet[9];
let props = Login::read(connect_flags, &packet, &mut 40)
.unwrap()
.unwrap();
assert_eq!(props.len(), 10);
}
#[test]
fn connect_props_read() {
let packet = sample_bytes();
let props = ConnectProperties::read(&packet, &mut 12).unwrap().unwrap();
assert_eq!(props.receive_maximum, Some(256));
}
#[test]
fn connect_props_len() {
let packet = sample_bytes();
let props = ConnectProperties::read(&packet, &mut 12).unwrap().unwrap();
assert_eq!(props.len(), 25);
}
#[test]
fn connect_props_read_2() {
let packet = sample_bytes();
let props = ConnectProperties::read(&packet, &mut 12).unwrap().unwrap();
assert_eq!(props.authentication_data, Some("auth-data".as_bytes()));
assert_eq!(props.max_packet_size, Some(265));
assert_eq!(props.receive_maximum, Some(256));
assert_eq!(props.session_expiry_interval, Some(120));
}
#[test]
fn will_props_read() {
let packet = sample_bytes_with_will();
let mut props = WillProperties::read(&packet, &mut 25).unwrap().unwrap();
assert_eq!(props.response_topic, Some("will_respond"));
assert_eq!(props.content_type, Some("text"));
assert_eq!(props.correlation_data, Some("1234".as_bytes()));
assert_eq!(props.message_expiry_interval, Some(120));
assert_eq!(props.payload_format_indicator, Some(1));
assert_eq!(props.delay_interval, Some(5));
assert_eq!(props.user_properties.pop(), Some(("sfilename", "test.txt")));
}
#[test]
fn will_props_len() {
let packet = sample_bytes_with_will();
let props = WillProperties::read(&packet, &mut 25).unwrap().unwrap();
assert_eq!(props.len(), 63);
}
#[test]
fn last_will_read() {
let packet = sample_bytes_with_will();
let connect_flags = packet[9];
let last_will = LastWill::read(connect_flags, &packet, &mut 25)
.unwrap()
.unwrap();
assert_eq!(last_will.topic, "will");
assert_eq!(last_will.message, "lastwill".as_bytes());
assert_eq!(last_will.qos, QoS::ExactlyOnce);
assert!(last_will.retain);
let mut props = last_will.properties.unwrap();
assert_eq!(props.response_topic, Some("will_respond"));
assert_eq!(props.content_type, Some("text"));
assert_eq!(props.correlation_data, Some("1234".as_bytes()));
assert_eq!(props.message_expiry_interval, Some(120));
assert_eq!(props.payload_format_indicator, Some(1));
assert_eq!(props.delay_interval, Some(5));
assert_eq!(props.user_properties.pop(), Some(("sfilename", "test.txt")));
}
#[test]
fn last_will_len() {
let packet = sample_bytes_with_will();
let connect_flags = packet[9];
let props = LastWill::read(connect_flags, &packet, &mut 25)
.unwrap()
.unwrap();
assert_eq!(props.len(), 80);
}
#[test]
fn connect_read() {
let packet = sample_bytes_with_will();
let connect = Connect::read(&packet, &mut 2).unwrap();
assert_eq!(connect.protocol, Protocol::V5);
assert_eq!(connect.keep_alive, 60);
assert_eq!(connect.client_id, "test_id");
assert!(!connect.clean_start);
let last_will = connect.last_will.unwrap();
assert_eq!(last_will.topic, "will");
assert_eq!(last_will.message, "lastwill".as_bytes());
assert_eq!(last_will.qos, QoS::ExactlyOnce);
assert!(last_will.retain);
let mut last_will_props = last_will.properties.unwrap();
assert_eq!(last_will_props.response_topic, Some("will_respond"));
assert_eq!(last_will_props.content_type, Some("text"));
assert_eq!(last_will_props.correlation_data, Some("1234".as_bytes()));
assert_eq!(last_will_props.message_expiry_interval, Some(120));
assert_eq!(last_will_props.payload_format_indicator, Some(1));
assert_eq!(last_will_props.delay_interval, Some(5));
assert_eq!(
last_will_props.user_properties.pop(),
Some(("sfilename", "test.txt"))
);
let login = connect.login.unwrap();
assert_eq!(login.username, "iz");
assert_eq!(login.password, "1234".as_bytes());
let connect_props = connect.properties.unwrap();
assert_eq!(connect_props.receive_maximum, Some(20));
}
#[test]
fn connect_len() {
let packet = sample_bytes_with_will();
let props = Connect::read(&packet, &mut 2).unwrap();
assert_eq!(props.len(), 113);
}
#[test]
fn last_will_write() {
let expected = &sample_bytes_with_will()[25..106];
let last_will = sample_last_will();
assert_eq!(last_will.len(), 80);
let mut buf = [0u8; 81];
let mut offset = 0;
last_will.write(&mut buf, &mut offset).unwrap();
assert_eq!(buf, expected);
}
#[test]
fn will_properties_write() {
let expected = &sample_bytes_with_will()[25..89];
let props = sample_will_properties();
assert_eq!(props.len(), 63);
let mut buf = [0u8; 64];
let mut offset = 0;
props.write(&mut buf, &mut offset).unwrap();
assert_eq!(buf, expected);
}
#[test]
fn login_to_bufer() {
let login = sample_login();
let expected = &sample_bytes_with_will()[105..];
let mut buf = [0u8; 10];
let mut offset = 0;
login.write(&mut buf, &mut offset);
assert_eq!(buf, expected);
}
#[test]
fn test_connect_write() {
let connect = Connect {
protocol: Protocol::V5,
keep_alive: 60,
client_id: "test_id",
clean_start: false,
last_will: Some(sample_last_will()),
login: Some(sample_login()),
properties: Some(ConnectProperties {
receive_maximum: Some(20),
..Default::default()
}),
};
let expected = &sample_bytes_with_will();
let mut buf = [0u8; 115];
assert_eq!(connect.len() + 2, expected.len());
let mut offset = 0;
connect.write(&mut buf, &mut offset).unwrap();
assert_eq!(buf, expected.as_slice());
}
fn sample<'a>() -> Connect<'a> {
let connect_properties = ConnectProperties {
session_expiry_interval: Some(1234),
receive_maximum: Some(432),
max_packet_size: Some(100),
topic_alias_max: Some(456),
request_response_info: Some(1),
request_problem_info: Some(1),
user_properties: heapless::Vec::<_, 32>::from_slice(&[("test", "test")]).unwrap(),
authentication_method: Some("test"),
authentication_data: Some(&[1, 2, 3, 4]),
};
let will_properties = WillProperties {
delay_interval: Some(1234),
payload_format_indicator: Some(0),
message_expiry_interval: Some(4321),
content_type: Some("test"),
response_topic: Some("topic"),
correlation_data: Some(&[1, 2, 3, 4]),
user_properties: heapless::Vec::<_, 32>::from_slice(&[("test", "test")]).unwrap(),
};
let will = LastWill {
topic: "mydevice/status",
message: &[b'd', b'e', b'a', b'd'],
qos: QoS::AtMostOnce,
retain: false,
properties: Some(will_properties),
};
let login = Login {
username: "matteo",
password: "collina".as_bytes(),
};
Connect {
protocol: Protocol::V5,
keep_alive: 0,
properties: Some(connect_properties),
client_id: "my-device",
clean_start: true,
last_will: Some(will),
login: Some(login),
}
}
fn sample_bytes_2() -> Vec<u8> {
vec![
0x10, 0x9d, 0x01, 0x00, 0x04, 0x4d, 0x51, 0x54, 0x54, 0x05, 0xc6, 0x00, 0x00, 0x2f, 0x11, 0x00, 0x00, 0x04, 0xd2, 0x21, 0x01, 0xb0, 0x27, 0x00, 0x00, 0x00, 0x64, 0x22, 0x01, 0xc8, 0x19, 0x01, 0x17, 0x01, 0x15, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x16, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
0x74, 0x00, 0x09, 0x6d, 0x79, 0x2d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x10, 0xe1, 0x03, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x00, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x09, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, 0x18, 0x00, 0x00, 0x04, 0xd2, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
0x74, 0x00, 0x0f, 0x6d, 0x79, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x73, 0x74, 0x61,
0x74, 0x75, 0x73, 0x00, 0x04, 0x64, 0x65, 0x61, 0x64, 0x00, 0x06, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x6f, 0x00, 0x07, 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x61, ]
}
#[test]
fn connect_encode() {
let expected = sample_bytes_2();
let mut buf = [0u8; 160];
sample().write(&mut buf, &mut 0).unwrap();
assert_eq!(expected, buf);
}
#[test]
fn connect_decode() {
let bytes = sample_bytes_2();
let expected = sample();
let actual = Connect::read(&bytes, &mut 3).unwrap();
assert_eq!(expected, actual);
}
}