use super::property::check_property_type_list;
use super::{Properties, PropertyType, ReasonCode};
use crate::{
ByteArray, DecodeError, DecodePacket, EncodeError, EncodePacket, FixedHeader, Packet,
PacketType, VarIntError,
};
#[allow(clippy::module_name_repetitions)]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ConnectAckPacket {
session_present: bool,
reason_code: ReasonCode,
properties: Properties,
}
pub const CONNECT_REASONS: &[ReasonCode] = &[
ReasonCode::Success,
ReasonCode::UnspecifiedError,
ReasonCode::MalformedPacket,
ReasonCode::ProtocolError,
ReasonCode::ImplementationSpecificError,
ReasonCode::UnsupportedProtocolVersion,
ReasonCode::ClientIdentifierNotValid,
ReasonCode::BadUserNameOrPassword,
ReasonCode::NotAuthorized,
ReasonCode::ServerUnavailable,
ReasonCode::ServerBusy,
ReasonCode::Banned,
ReasonCode::BadAuthenticationMethod,
ReasonCode::TopicNameInvalid,
ReasonCode::PacketTooLarge,
ReasonCode::QuotaExceeded,
ReasonCode::PayloadFormatInvalid,
ReasonCode::QoSNotSupported,
ReasonCode::UseAnotherServer,
ReasonCode::ServerMoved,
ReasonCode::ConnectionRateExceeded,
];
pub const CONNECT_ACK_PROPERTIES: &[PropertyType] = &[
PropertyType::SessionExpiryInterval,
PropertyType::ReceiveMaximum,
PropertyType::MaximumQoS,
PropertyType::RetainAvailable,
PropertyType::MaximumPacketSize,
PropertyType::AssignedClientIdentifier,
PropertyType::TopicAliasMaximum,
PropertyType::ReasonString,
PropertyType::UserProperty,
PropertyType::WildcardSubscriptionAvailable,
PropertyType::SubscriptionIdentifierAvailable,
PropertyType::SharedSubscriptionAvailable,
PropertyType::ServerKeepAlive,
PropertyType::ResponseInformation,
PropertyType::ServerReference,
PropertyType::AuthenticationMethod,
PropertyType::AuthenticationData,
];
impl ConnectAckPacket {
#[must_use]
pub fn new(mut session_present: bool, reason_code: ReasonCode) -> Self {
if reason_code != ReasonCode::Success {
session_present = false;
}
Self {
session_present,
reason_code,
properties: Properties::new(),
}
}
pub fn set_reason_code(&mut self, reason_code: ReasonCode) -> Result<&mut Self, EncodeError> {
if !CONNECT_REASONS.contains(&reason_code) {
return Err(EncodeError::InvalidReasonCode);
}
if reason_code != ReasonCode::Success {
self.session_present = false;
}
self.reason_code = reason_code;
Ok(self)
}
#[must_use]
pub const fn reason_code(&self) -> ReasonCode {
self.reason_code
}
pub fn set_session_present(&mut self, present: bool) -> &mut Self {
self.session_present = present;
self
}
#[must_use]
pub const fn session_present(&self) -> bool {
self.session_present
}
pub fn properties_mut(&mut self) -> &mut Properties {
&mut self.properties
}
#[must_use]
pub const fn properties(&self) -> &Properties {
&self.properties
}
}
impl DecodePacket for ConnectAckPacket {
fn decode(ba: &mut ByteArray) -> Result<Self, DecodeError> {
let fixed_header = FixedHeader::decode(ba)?;
assert_eq!(fixed_header.packet_type(), PacketType::ConnectAck);
let ack_flags = ba.read_byte()?;
let session_present = ack_flags & 0b0000_0001 == 0b0000_0001;
let reason_code = ReasonCode::decode(ba)?;
if !CONNECT_REASONS.contains(&reason_code) {
log::error!("Invalid reason code {:?}", reason_code);
return Err(DecodeError::InvalidReasonCode);
}
let properties = Properties::decode(ba)?;
if let Err(property_type) =
check_property_type_list(properties.props(), CONNECT_ACK_PROPERTIES)
{
log::error!(
"v5/ConnectAckPacket: property type {:?} cannot be used in properties!",
property_type
);
return Err(DecodeError::InvalidPropertyType);
}
Ok(Self {
session_present,
reason_code,
properties,
})
}
}
impl EncodePacket for ConnectAckPacket {
fn encode(&self, buf: &mut Vec<u8>) -> Result<usize, EncodeError> {
let old_len = buf.len();
let remaining_length = 1 + ReasonCode::bytes() + self.properties.bytes();
let fixed_header = FixedHeader::new(PacketType::ConnectAck, remaining_length)?;
fixed_header.encode(buf)?;
let ack_flags = u8::from(self.session_present);
buf.push(ack_flags);
self.reason_code.encode(buf)?;
self.properties.encode(buf)?;
Ok(buf.len() - old_len)
}
}
impl Packet for ConnectAckPacket {
fn packet_type(&self) -> PacketType {
PacketType::ConnectAck
}
fn bytes(&self) -> Result<usize, VarIntError> {
let remaining_length = 1 + ReasonCode::bytes() + self.properties.bytes();
let fixed_header = FixedHeader::new(PacketType::ConnectAck, remaining_length)?;
Ok(fixed_header.bytes() + remaining_length)
}
}