ntex_mqtt/v3/codec/
packet.rs

1use std::{fmt, num::NonZeroU16};
2
3use ntex_bytes::{ByteString, Bytes};
4
5use crate::types::{packet_type, QoS};
6
7prim_enum! {
8    /// Connect Return Code
9    pub enum ConnectAckReason {
10        /// Connection accepted
11        ConnectionAccepted = 0,
12        /// Connection Refused, unacceptable protocol version
13        UnacceptableProtocolVersion = 1,
14        /// Connection Refused, identifier rejected
15        IdentifierRejected = 2,
16        /// Connection Refused, Server unavailable
17        ServiceUnavailable = 3,
18        /// Connection Refused, bad user name or password
19        BadUserNameOrPassword = 4,
20        /// Connection Refused, not authorized
21        NotAuthorized = 5,
22        /// Reserved
23        Reserved = 6
24    }
25}
26
27impl ConnectAckReason {
28    pub fn reason(self) -> &'static str {
29        match self {
30            ConnectAckReason::ConnectionAccepted => "Connection Accepted",
31            ConnectAckReason::UnacceptableProtocolVersion => {
32                "Connection Refused, unacceptable protocol version"
33            }
34            ConnectAckReason::IdentifierRejected => "Connection Refused, identifier rejected",
35            ConnectAckReason::ServiceUnavailable => "Connection Refused, Server unavailable",
36            ConnectAckReason::BadUserNameOrPassword => {
37                "Connection Refused, bad user name or password"
38            }
39            ConnectAckReason::NotAuthorized => "Connection Refused, not authorized",
40            _ => "Connection Refused",
41        }
42    }
43}
44
45#[derive(Debug, PartialEq, Eq, Clone)]
46/// Connection Will
47pub struct LastWill {
48    /// the QoS level to be used when publishing the Will Message.
49    pub qos: QoS,
50    /// the Will Message is to be Retained when it is published.
51    pub retain: bool,
52    /// the Will Topic
53    pub topic: ByteString,
54    /// defines the Application Message that is to be published to the Will Topic
55    pub message: Bytes,
56}
57
58#[derive(Default, Debug, PartialEq, Eq, Clone)]
59/// Connect packet content
60pub struct Connect {
61    /// the handling of the Session state.
62    pub clean_session: bool,
63    /// a time interval measured in seconds.
64    pub keep_alive: u16,
65    /// Will Message be stored on the Server and associated with the Network Connection.
66    pub last_will: Option<LastWill>,
67    /// identifies the Client to the Server.
68    pub client_id: ByteString,
69    /// username can be used by the Server for authentication and authorization.
70    pub username: Option<ByteString>,
71    /// password can be used by the Server for authentication and authorization.
72    pub password: Option<Bytes>,
73}
74
75impl Connect {
76    /// Set client_id value
77    pub fn client_id<T>(mut self, client_id: T) -> Self
78    where
79        ByteString: From<T>,
80    {
81        self.client_id = client_id.into();
82        self
83    }
84}
85
86#[derive(Debug, PartialEq, Eq, Clone)]
87/// Publish message
88pub struct Publish {
89    /// this might be re-delivery of an earlier attempt to send the Packet.
90    pub dup: bool,
91    pub retain: bool,
92    /// the level of assurance for delivery of an Application Message.
93    pub qos: QoS,
94    /// the information channel to which payload data is published.
95    pub topic: ByteString,
96    /// only present in PUBLISH Packets where the QoS level is 1 or 2.
97    pub packet_id: Option<NonZeroU16>,
98    /// publish packet payload size
99    pub payload_size: u32,
100}
101
102#[derive(Debug, PartialEq, Eq, Copy, Clone)]
103/// ConnectAck message
104pub struct ConnectAck {
105    pub return_code: ConnectAckReason,
106    /// enables a Client to establish whether the Client and Server have a consistent view
107    /// about whether there is already stored Session state.
108    pub session_present: bool,
109}
110
111#[derive(Debug, PartialEq, Eq, Copy, Clone)]
112/// Subscribe Return Code
113pub enum SubscribeReturnCode {
114    Success(QoS),
115    Failure,
116}
117
118#[derive(Debug, PartialEq, Eq, Clone)]
119/// MQTT Control Packets
120pub enum Packet {
121    /// Client request to connect to Server
122    Connect(Box<Connect>),
123    /// Connect acknowledgment
124    ConnectAck(ConnectAck),
125    /// Publish acknowledgment
126    PublishAck {
127        /// Packet Identifier
128        packet_id: NonZeroU16,
129    },
130    /// Publish received (assured delivery part 1)
131    PublishReceived {
132        /// Packet Identifier
133        packet_id: NonZeroU16,
134    },
135    /// Publish release (assured delivery part 2)
136    PublishRelease {
137        /// Packet Identifier
138        packet_id: NonZeroU16,
139    },
140    /// Publish complete (assured delivery part 3)
141    PublishComplete {
142        /// Packet Identifier
143        packet_id: NonZeroU16,
144    },
145    /// Client subscribe request
146    Subscribe {
147        /// Packet Identifier
148        packet_id: NonZeroU16,
149        /// the list of Topic Filters and QoS to which the Client wants to subscribe.
150        topic_filters: Vec<(ByteString, QoS)>,
151    },
152    /// Subscribe acknowledgment
153    SubscribeAck {
154        packet_id: NonZeroU16,
155        /// corresponds to a Topic Filter in the SUBSCRIBE Packet being acknowledged.
156        status: Vec<SubscribeReturnCode>,
157    },
158    /// Unsubscribe request
159    Unsubscribe {
160        /// Packet Identifier
161        packet_id: NonZeroU16,
162        /// the list of Topic Filters that the Client wishes to unsubscribe from.
163        topic_filters: Vec<ByteString>,
164    },
165    /// Unsubscribe acknowledgment
166    UnsubscribeAck {
167        /// Packet Identifier
168        packet_id: NonZeroU16,
169    },
170    /// PING request
171    PingRequest,
172    /// PING response
173    PingResponse,
174    /// Client is disconnecting
175    Disconnect,
176}
177
178impl From<Connect> for Packet {
179    fn from(val: Connect) -> Packet {
180        Packet::Connect(Box::new(val))
181    }
182}
183
184impl Packet {
185    pub fn packet_type(&self) -> u8 {
186        match self {
187            Packet::Connect(_) => packet_type::CONNECT,
188            Packet::ConnectAck { .. } => packet_type::CONNACK,
189            Packet::PublishAck { .. } => packet_type::PUBACK,
190            Packet::PublishReceived { .. } => packet_type::PUBREC,
191            Packet::PublishRelease { .. } => packet_type::PUBREL,
192            Packet::PublishComplete { .. } => packet_type::PUBCOMP,
193            Packet::Subscribe { .. } => packet_type::SUBSCRIBE,
194            Packet::SubscribeAck { .. } => packet_type::SUBACK,
195            Packet::Unsubscribe { .. } => packet_type::UNSUBSCRIBE,
196            Packet::UnsubscribeAck { .. } => packet_type::UNSUBACK,
197            Packet::PingRequest => packet_type::PINGREQ,
198            Packet::PingResponse => packet_type::PINGRESP,
199            Packet::Disconnect => packet_type::DISCONNECT,
200        }
201    }
202}
203
204#[cfg(test)]
205mod tests {
206    use super::*;
207
208    #[test]
209    fn test_ack_reason() {
210        assert_eq!(ConnectAckReason::ConnectionAccepted.reason(), "Connection Accepted");
211        assert_eq!(
212            ConnectAckReason::UnacceptableProtocolVersion.reason(),
213            "Connection Refused, unacceptable protocol version"
214        );
215        assert_eq!(
216            ConnectAckReason::IdentifierRejected.reason(),
217            "Connection Refused, identifier rejected"
218        );
219        assert_eq!(
220            ConnectAckReason::ServiceUnavailable.reason(),
221            "Connection Refused, Server unavailable"
222        );
223        assert_eq!(
224            ConnectAckReason::BadUserNameOrPassword.reason(),
225            "Connection Refused, bad user name or password"
226        );
227        assert_eq!(
228            ConnectAckReason::NotAuthorized.reason(),
229            "Connection Refused, not authorized"
230        );
231    }
232}