use crate::{
ByteArray, DecodeError, DecodePacket, EncodeError, EncodePacket, FixedHeader, Packet,
PacketType, VarIntError,
};
#[repr(u8)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ConnectReturnCode {
#[default]
Accepted = 0,
UnacceptedProtocol = 1,
IdentifierRejected = 2,
ServerUnavailable = 3,
MalformedUsernamePassword = 4,
Unauthorized = 5,
Reserved = 6,
}
impl From<u8> for ConnectReturnCode {
fn from(v: u8) -> Self {
match v {
0 => Self::Accepted,
1 => Self::UnacceptedProtocol,
2 => Self::IdentifierRejected,
3 => Self::ServerUnavailable,
4 => Self::MalformedUsernamePassword,
5 => Self::Unauthorized,
_ => Self::Reserved,
}
}
}
#[allow(clippy::module_name_repetitions)]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ConnectAckPacket {
session_present: bool,
return_code: ConnectReturnCode,
}
impl ConnectAckPacket {
#[must_use]
pub fn new(mut session_present: bool, return_code: ConnectReturnCode) -> Self {
if return_code != ConnectReturnCode::Accepted {
session_present = false;
}
Self {
session_present,
return_code,
}
}
pub fn set_return_code(&mut self, code: ConnectReturnCode) -> &mut Self {
self.return_code = code;
self
}
#[must_use]
pub const fn return_code(&self) -> ConnectReturnCode {
self.return_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
}
}
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 return_code = ConnectReturnCode::from(ba.read_byte()?);
Ok(Self {
session_present,
return_code,
})
}
}
impl EncodePacket for ConnectAckPacket {
fn encode(&self, buf: &mut Vec<u8>) -> Result<usize, EncodeError> {
let old_len = buf.len();
let fixed_header = FixedHeader::new(PacketType::ConnectAck, 2)?;
fixed_header.encode(buf)?;
let ack_flags = u8::from(self.session_present);
buf.push(ack_flags);
buf.push(self.return_code as u8);
Ok(buf.len() - old_len)
}
}
impl Packet for ConnectAckPacket {
fn packet_type(&self) -> PacketType {
PacketType::ConnectAck
}
fn bytes(&self) -> Result<usize, VarIntError> {
let fixed_header = FixedHeader::new(PacketType::ConnectAck, 2)?;
Ok(fixed_header.bytes() + fixed_header.remaining_length())
}
}
#[cfg(test)]
mod tests {
use super::{ByteArray, ConnectAckPacket, DecodePacket};
#[test]
fn test_decode() {
let buf: Vec<u8> = vec![0x20, 0x02, 0x00, 0x00];
let mut ba = ByteArray::new(&buf);
let packet = ConnectAckPacket::decode(&mut ba);
assert!(packet.is_ok());
let packet = packet.unwrap();
assert!(!packet.session_present);
}
}