use core::{
default::Default,
convert::TryFrom,
cmp::min,
result::Result,
};
use crate::{
fixed_header::{self, FixedHeader},
variable_header::{self, VariableHeader},
payload::{self, Payload},
status::Status,
error::{DecodeError, EncodeError},
codec::{Decodable, Encodable},
qos,
};
#[derive(Debug)]
#[allow(dead_code)]
pub struct Packet<'a> {
fixed_header: FixedHeader,
variable_header: Option<VariableHeader<'a>>,
payload: Payload<'a>,
}
impl<'a> Packet<'a> {
pub fn connect(variable_header: variable_header::connect::Connect<'a>, payload: payload::connect::Connect<'a>) -> Result<Self, EncodeError> {
Self::packet(
fixed_header::PacketType::Connect,
fixed_header::PacketFlags::CONNECT,
Some(variable_header::VariableHeader::Connect(variable_header)),
payload::Payload::Connect(payload)
)
}
pub fn subscribe(variable_header: variable_header::packet_identifier::PacketIdentifier, payload: payload::subscribe::Subscribe<'a>) -> Result<Self, EncodeError> {
Self::packet(
fixed_header::PacketType::Subscribe,
fixed_header::PacketFlags::SUBSCRIBE,
Some(variable_header::VariableHeader::Subscribe(variable_header)),
payload::Payload::Subscribe(payload)
)
}
pub fn publish(flags: fixed_header::PublishFlags, variable_header: variable_header::publish::Publish<'a>, payload: &'a [u8]) -> Result<Self, EncodeError> {
assert!(flags.qos().expect("valid qos") == qos::QoS::AtMostOnce || variable_header.packet_identifier().is_some());
Self::packet(
fixed_header::PacketType::Publish,
flags.into(),
Some(variable_header::VariableHeader::Publish(variable_header)),
payload::Payload::Bytes(payload)
)
}
pub fn puback(variable_header: variable_header::packet_identifier::PacketIdentifier) -> Result<Self, EncodeError> {
Self::packet(
fixed_header::PacketType::Puback,
fixed_header::PacketFlags::PUBACK,
Some(variable_header::VariableHeader::Puback(variable_header)),
Default::default(),
)
}
pub fn pingreq() -> Self {
Self {
fixed_header: FixedHeader::new(
fixed_header::PacketType::Pingreq,
fixed_header::PacketFlags::PINGREQ,
0,
),
variable_header: None,
payload: Default::default(),
}
}
pub fn pingresp() -> Self {
Self {
fixed_header: FixedHeader::new(
fixed_header::PacketType::Pingresp,
fixed_header::PacketFlags::PINGRESP,
0,
),
variable_header: None,
payload: Default::default(),
}
}
fn packet(r#type: fixed_header::PacketType, flags: fixed_header::PacketFlags, variable_header: Option<VariableHeader<'a>>, payload: Payload<'a>) -> Result<Self, EncodeError> {
let len = u32::try_from(
variable_header.as_ref().map(VariableHeader::encoded_len).unwrap_or(0) +
payload.encoded_len()
)?;
Ok(Self {
fixed_header: FixedHeader::new(
r#type,
flags,
len,
),
variable_header: variable_header,
payload: payload,
})
}
pub fn fixed_header(&self) -> &FixedHeader {
&self.fixed_header
}
pub fn variable_header(&self) -> &Option<VariableHeader> {
&self.variable_header
}
pub fn payload(&self) -> &Payload {
&self.payload
}
}
impl<'a> Decodable<'a> for Packet<'a> {
fn decode(bytes: &'a [u8]) -> Result<Status<(usize, Self)>, DecodeError> {
let (fixed_header_offset, fixed_header) = read!(FixedHeader::decode, bytes, 0);
let (variable_header_consumed, variable_header) = if let Some(result) = VariableHeader::decode(fixed_header.r#type(), fixed_header.flags(), &bytes[fixed_header_offset..]) {
let (variable_header_offset, variable_header) = complete!(result);
(variable_header_offset, Some(variable_header))
} else {
(0, None)
};
let payload_len = fixed_header.len() as usize - variable_header_consumed;
let available = bytes.len() - (fixed_header_offset + variable_header_consumed);
let needed = payload_len - min(available, payload_len);
if needed > 0 {
return Ok(Status::Partial(needed));
}
let payload_bytes = &bytes[fixed_header_offset+variable_header_consumed..fixed_header_offset+variable_header_consumed+payload_len];
let payload = if let Some(result) = Payload::decode(fixed_header.r#type(), payload_bytes) {
match result {
Err(e) => return Err(e),
Ok(Status::Partial(n)) => return Ok(Status::Partial(n)),
Ok(Status::Complete((_, payload))) => payload,
}
} else {
payload::Payload::Bytes(payload_bytes)
};
Ok(Status::Complete((fixed_header_offset + fixed_header.len() as usize, Self {
fixed_header,
variable_header,
payload,
})))
}
}
impl<'a> Encodable for Packet<'a> {
fn encoded_len(&self) -> usize {
self.fixed_header.encoded_len() + self.fixed_header.len() as usize
}
fn encode(&self, bytes: &mut [u8]) -> Result<usize, EncodeError> {
let mut offset = 0;
offset = {
let o = self.fixed_header.encode(&mut bytes[offset..])?;
offset + o
};
if let Some(ref variable_header) = self.variable_header {
offset = {
let o = variable_header.encode(&mut bytes[offset..])?;
offset + o
};
}
let offset = {
let o = self.payload.encode(&mut bytes[offset..])?;
offset + o
};
Ok(offset)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encode_publish() {
let payload = b"{}";
assert_eq!(2, payload.len());
let mut publish_flags = fixed_header::PublishFlags::default();
publish_flags.set_qos(qos::QoS::AtLeastOnce);
let publish_id = 2;
let publish = Packet::publish(
publish_flags,
variable_header::publish::Publish::new(
"a/b",
Some(publish_id),
),
payload
).expect("valid packet");
assert_eq!(11, publish.encoded_len());
assert_eq!(2, publish.fixed_header().encoded_len());
assert_eq!(9, publish.fixed_header().len());
assert_eq!(7, publish.variable_header().as_ref().expect("variable header").encoded_len());
assert_eq!(2, publish.payload().encoded_len());
}
#[test]
fn encode_subscribe() {
let subscribe_id = 1;
let sub = Packet::subscribe(
variable_header::packet_identifier::PacketIdentifier::new(subscribe_id),
payload::subscribe::Subscribe::new(&[
("c/a", qos::QoS::AtMostOnce),
("c/b", qos::QoS::AtLeastOnce),
("c/c", qos::QoS::ExactlyOnce),
]),
).expect("valid packet");
assert_eq!(22, sub.encoded_len());
assert_eq!(2, sub.fixed_header().encoded_len());
assert_eq!(20, sub.fixed_header().len());
assert_eq!(2, sub.variable_header().as_ref().expect("variable header").encoded_len());
assert_eq!(18, sub.payload().encoded_len());
}
}