use super::property::check_property_type_list;
use super::{Properties, PropertyType};
use crate::{
ByteArray, DecodeError, DecodePacket, EncodeError, EncodePacket, FixedHeader, Packet, PacketId,
PacketType, SubTopic, VarIntError,
};
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct UnsubscribePacket {
packet_id: PacketId,
properties: Properties,
topics: Vec<SubTopic>,
}
impl UnsubscribePacket {
pub fn new(topic: &str, packet_id: PacketId) -> Result<Self, EncodeError> {
let topic = SubTopic::new(topic)?;
Ok(Self {
packet_id,
properties: Properties::new(),
topics: vec![topic],
})
}
pub fn with_topics(topics: &[&str], packet_id: PacketId) -> Result<Self, EncodeError> {
let mut topics_result = Vec::with_capacity(topics.len());
for topic in topics {
let topic = SubTopic::new(topic)?;
topics_result.push(topic);
}
Ok(Self {
packet_id,
properties: Properties::new(),
topics: topics_result,
})
}
pub fn set_packet_id(&mut self, packet_id: PacketId) -> &mut Self {
self.packet_id = packet_id;
self
}
#[must_use]
pub const fn packet_id(&self) -> PacketId {
self.packet_id
}
pub fn properties_mut(&mut self) -> &mut Properties {
&mut self.properties
}
#[must_use]
pub const fn properties(&self) -> &Properties {
&self.properties
}
pub fn add_topic(&mut self, topic: &str) -> Result<&mut Self, EncodeError> {
let topic = SubTopic::new(topic)?;
self.topics.push(topic);
Ok(self)
}
pub fn set_topics(&mut self, topics: &[&str]) -> Result<&mut Self, EncodeError> {
self.topics.clear();
for topic in topics {
let topic = SubTopic::new(topic)?;
self.topics.push(topic);
}
Ok(self)
}
#[must_use]
pub fn topics(&self) -> &[SubTopic] {
&self.topics
}
pub fn mut_topics(&mut self) -> &mut Vec<SubTopic> {
&mut self.topics
}
fn get_fixed_header(&self) -> Result<FixedHeader, VarIntError> {
let mut remaining_length: usize = PacketId::bytes() + self.properties.bytes();
for topic in &self.topics {
remaining_length += topic.bytes();
}
FixedHeader::new(PacketType::Unsubscribe, remaining_length)
}
}
pub const UNSUBSCRIBE_PROPERTIES: &[PropertyType] = &[PropertyType::UserProperty];
impl DecodePacket for UnsubscribePacket {
fn decode(ba: &mut ByteArray) -> Result<Self, DecodeError> {
let fixed_header = FixedHeader::decode(ba)?;
if fixed_header.packet_type() != PacketType::Unsubscribe {
return Err(DecodeError::InvalidPacketType);
}
let packet_id = PacketId::decode(ba)?;
if packet_id.value() == 0 {
return Err(DecodeError::InvalidPacketId);
}
let properties = Properties::decode(ba)?;
if let Err(property_type) =
check_property_type_list(properties.props(), UNSUBSCRIBE_PROPERTIES)
{
log::error!(
"v5/UnsubscribePacket: property type {:?} cannot be used in properties!",
property_type
);
return Err(DecodeError::InvalidPropertyType);
}
let mut remaining_length = PacketId::bytes() + properties.bytes();
let mut topics = Vec::new();
while remaining_length < fixed_header.remaining_length() {
let topic = SubTopic::decode(ba)?;
remaining_length += topic.bytes();
topics.push(topic);
}
if topics.is_empty() {
return Err(DecodeError::EmptyTopicFilter);
}
Ok(Self {
packet_id,
properties,
topics,
})
}
}
impl EncodePacket for UnsubscribePacket {
fn encode(&self, v: &mut Vec<u8>) -> Result<usize, EncodeError> {
let old_len = v.len();
let fixed_header = self.get_fixed_header()?;
fixed_header.encode(v)?;
self.packet_id.encode(v)?;
self.properties.encode(v)?;
for topic in &self.topics {
topic.encode(v)?;
}
Ok(v.len() - old_len)
}
}
impl Packet for UnsubscribePacket {
fn packet_type(&self) -> PacketType {
PacketType::Unsubscribe
}
fn bytes(&self) -> Result<usize, VarIntError> {
let fixed_header = self.get_fixed_header()?;
Ok(fixed_header.bytes() + fixed_header.remaining_length())
}
}