use core::{ops::BitAnd, slice::Iter};
#[repr(u8)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ConnectCode {
Accept = 0,
BadProto = 1,
BadId = 2,
Unavailable = 3,
BadCreds = 4,
NotAuth = 5,
}
impl ConnectCode {
pub const fn is_accept(&self) -> bool {
matches!(self, Self::Accept)
}
}
impl From<ConnectCode> for u8 {
#[inline]
fn from(val: ConnectCode) -> Self {
val as u8
}
}
impl TryFrom<u8> for ConnectCode {
type Error = u8;
fn try_from(val: u8) -> Result<Self, Self::Error> {
match val {
x if x == ConnectCode::Accept as u8 => Ok(ConnectCode::Accept),
x if x == ConnectCode::BadProto as u8 => Ok(ConnectCode::BadProto),
x if x == ConnectCode::BadId as u8 => Ok(ConnectCode::BadId),
x if x == ConnectCode::Unavailable as u8 => Ok(ConnectCode::Unavailable),
x if x == ConnectCode::BadCreds as u8 => Ok(ConnectCode::BadCreds),
x if x == ConnectCode::NotAuth as u8 => Ok(ConnectCode::NotAuth),
x => Err(x),
}
}
}
pub const CONNACK_LEN: usize = 4;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ConnectError {
Underflow,
InvalidCtrlPkt(u8),
InvalidLen(u8),
InvalidCode(u8),
}
impl core::fmt::Display for ConnectError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ConnectError::Underflow => write!(f, "packet length is too short"),
ConnectError::InvalidCtrlPkt(x) => write!(f, "packet type is not CONNACK: {:#02X}", x),
ConnectError::InvalidLen(x) => write!(f, "remaining length is invalid: {:#02X}", x),
ConnectError::InvalidCode(x) => write!(f, "CONNACK code is invalid: {:#02X}", x),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ConnectError {}
pub struct ConnackResult {
session_present: bool,
code: ConnectCode,
}
impl ConnackResult {
pub fn from_buf(buf: &[u8]) -> Result<Self, ConnectError> {
const EXPECTED_BYTE_1: u8 = (super::CtrlPkt::CONNACK as u8) << 4;
const EXPECTED_BYTE_2: u8 = 2;
let mut bytes: Iter<u8> = buf.iter();
let byte1: u8 = *bytes.next().ok_or(ConnectError::Underflow)?;
if byte1 != EXPECTED_BYTE_1 {
return Err(ConnectError::InvalidCtrlPkt(byte1));
}
let byte2: u8 = *bytes.next().ok_or(ConnectError::Underflow)?;
if byte2 != EXPECTED_BYTE_2 {
return Err(ConnectError::InvalidLen(byte2));
}
let session_present: bool = bytes
.next()
.ok_or(ConnectError::Underflow)?
.bitand(0b1)
.eq(&0b1);
let code: ConnectCode =
ConnectCode::try_from(*bytes.next().ok_or(ConnectError::Underflow)?)
.map_err(ConnectError::InvalidCode)?;
Ok(Self {
session_present,
code,
})
}
pub const fn session_present(&self) -> bool {
self.session_present
}
pub const fn code(&self) -> ConnectCode {
self.code
}
}