use std::num::NonZeroU16;
use bytes::Bytes;
use bytestring::ByteString;
use serde::{Deserialize, Serialize};
use crate::types::{packet_type, Protocol, QoS};
prim_enum! {
#[derive(Deserialize, Serialize)]
pub enum ConnectAckReason {
ConnectionAccepted = 0,
UnacceptableProtocolVersion = 1,
IdentifierRejected = 2,
ServiceUnavailable = 3,
BadUserNameOrPassword = 4,
NotAuthorized = 5,
Reserved = 6
}
}
impl From<ConnectAckReason> for u8 {
fn from(v: ConnectAckReason) -> Self {
match v {
ConnectAckReason::ConnectionAccepted => 0,
ConnectAckReason::UnacceptableProtocolVersion => 1,
ConnectAckReason::IdentifierRejected => 2,
ConnectAckReason::ServiceUnavailable => 3,
ConnectAckReason::BadUserNameOrPassword => 4,
ConnectAckReason::NotAuthorized => 5,
ConnectAckReason::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, Eq, Clone, Deserialize, Serialize)]
pub struct LastWill {
pub qos: QoS,
pub retain: bool,
pub topic: ByteString,
pub message: Bytes,
}
#[derive(Default, Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
pub struct Connect {
pub protocol: Protocol,
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
}
}
pub(crate) type Publish = crate::types::Publish;
#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub struct ConnectAck {
pub return_code: ConnectAckReason,
pub session_present: bool,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum SubscribeReturnCode {
Success(QoS),
Failure,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Packet {
Connect(Box<Connect>),
ConnectAck(ConnectAck),
Publish(Box<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(Box::new(val))
}
}
impl From<Publish> for Packet {
fn from(val: Publish) -> Packet {
Packet::Publish(Box::new(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");
}
}