Skip to main content

rmqtt_codec/v3/
packet.rs

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