use std::{fmt, num::NonZeroU16};
use ntex::util::{ByteString, Bytes};
use crate::types::{packet_type, QoS};
prim_enum! {
pub enum ConnectAckReason {
ConnectionAccepted = 0,
UnacceptableProtocolVersion = 1,
IdentifierRejected = 2,
ServiceUnavailable = 3,
BadUserNameOrPassword = 4,
NotAuthorized = 5,
Reserved = 6
}
}
impl ConnectAckReason {
pub fn reason(self) -> &'static str {
match self {
ConnectAckReason::ConnectionAccepted => "Connection Accepted",
ConnectAckReason::UnacceptableProtocolVersion => {
"Connection Refused, unacceptable protocol version"
}
ConnectAckReason::IdentifierRejected => "Connection Refused, identifier rejected",
ConnectAckReason::ServiceUnavailable => "Connection Refused, Server unavailable",
ConnectAckReason::BadUserNameOrPassword => {
"Connection Refused, bad user name or password"
}
ConnectAckReason::NotAuthorized => "Connection Refused, not authorized",
_ => "Connection Refused",
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct LastWill {
pub qos: QoS,
pub retain: bool,
pub topic: ByteString,
pub message: Bytes,
}
#[derive(Default, Debug, PartialEq, Clone)]
pub struct Connect {
pub clean_session: bool,
pub keep_alive: u16,
pub last_will: Option<LastWill>,
pub client_id: ByteString,
pub username: Option<ByteString>,
pub password: Option<Bytes>,
}
impl Connect {
pub fn client_id<T>(mut self, client_id: T) -> Self
where
ByteString: From<T>,
{
self.client_id = client_id.into();
self
}
}
#[derive(PartialEq, Clone)]
pub struct Publish {
pub dup: bool,
pub retain: bool,
pub qos: QoS,
pub topic: ByteString,
pub packet_id: Option<NonZeroU16>,
pub payload: Bytes,
}
impl fmt::Debug for Publish {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Publish")
.field("packet_id", &self.packet_id)
.field("topic", &self.topic)
.field("dup", &self.dup)
.field("retain", &self.retain)
.field("qos", &self.qos)
.field("payload", &"<REDACTED>")
.finish()
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum SubscribeReturnCode {
Success(QoS),
Failure,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Packet {
Connect(Connect),
ConnectAck {
session_present: bool,
return_code: ConnectAckReason,
},
Publish(Publish),
PublishAck {
packet_id: NonZeroU16,
},
PublishReceived {
packet_id: NonZeroU16,
},
PublishRelease {
packet_id: NonZeroU16,
},
PublishComplete {
packet_id: NonZeroU16,
},
Subscribe {
packet_id: NonZeroU16,
topic_filters: Vec<(ByteString, QoS)>,
},
SubscribeAck {
packet_id: NonZeroU16,
status: Vec<SubscribeReturnCode>,
},
Unsubscribe {
packet_id: NonZeroU16,
topic_filters: Vec<ByteString>,
},
UnsubscribeAck {
packet_id: NonZeroU16,
},
PingRequest,
PingResponse,
Disconnect,
}
impl From<Connect> for Packet {
fn from(val: Connect) -> Packet {
Packet::Connect(val)
}
}
impl From<Publish> for Packet {
fn from(val: Publish) -> Packet {
Packet::Publish(val)
}
}
impl Packet {
pub fn packet_type(&self) -> u8 {
match self {
Packet::Connect(_) => packet_type::CONNECT,
Packet::ConnectAck { .. } => packet_type::CONNACK,
Packet::Publish(_) => packet_type::PUBLISH_START,
Packet::PublishAck { .. } => packet_type::PUBACK,
Packet::PublishReceived { .. } => packet_type::PUBREC,
Packet::PublishRelease { .. } => packet_type::PUBREL,
Packet::PublishComplete { .. } => packet_type::PUBCOMP,
Packet::Subscribe { .. } => packet_type::SUBSCRIBE,
Packet::SubscribeAck { .. } => packet_type::SUBACK,
Packet::Unsubscribe { .. } => packet_type::UNSUBSCRIBE,
Packet::UnsubscribeAck { .. } => packet_type::UNSUBACK,
Packet::PingRequest => packet_type::PINGREQ,
Packet::PingResponse => packet_type::PINGRESP,
Packet::Disconnect => packet_type::DISCONNECT,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ack_reason() {
assert_eq!(ConnectAckReason::ConnectionAccepted.reason(), "Connection Accepted");
assert_eq!(
ConnectAckReason::UnacceptableProtocolVersion.reason(),
"Connection Refused, unacceptable protocol version"
);
assert_eq!(
ConnectAckReason::IdentifierRejected.reason(),
"Connection Refused, identifier rejected"
);
assert_eq!(
ConnectAckReason::ServiceUnavailable.reason(),
"Connection Refused, Server unavailable"
);
assert_eq!(
ConnectAckReason::BadUserNameOrPassword.reason(),
"Connection Refused, bad user name or password"
);
assert_eq!(
ConnectAckReason::NotAuthorized.reason(),
"Connection Refused, not authorized"
);
}
}