mqtt_tiny/packets/
connect.rs

1//! MQTT [`CONNECT`](https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718033)
2
3use crate::anyvec::AnyVec;
4use crate::coding::encoder::{BytesIter, OptionalBytesIter, PacketLenIter, U16Iter, U8Iter, Unit};
5use crate::coding::length::Length;
6use crate::coding::{Decoder, Encoder};
7use crate::err;
8use crate::error::{Data, DecoderError, MemoryError};
9use crate::packets::TryFromIterator;
10use core::iter::Chain;
11
12/// An MQTT [`CONNECT` packet](https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718033)
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct Connect<Bytes> {
15    /// The seconds to keep the connection alive
16    keep_alive_secs: u16,
17    /// When set to `true` the client and server need not process the deletion of state atomically
18    clean_session: bool,
19    /// This bit specifies if the will message is to be Retained when it is published
20    will_retain: bool,
21    /// The QoS level to be used when publishing the will message
22    ///
23    /// # QoS Levels
24    /// Valid QoS levels are:
25    ///  - `0`: At most one delivery
26    ///  - `1`: At least one delivery
27    ///  - `2`: Exactly one delivery
28    will_qos: u8,
29    /// The client identifier
30    ///
31    /// # Important
32    /// MQTT allows only the characters `0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`, and the
33    /// identifier should not be longer than 23 bytes.
34    client_id: Bytes,
35    /// The will topic
36    will_topic: Option<Bytes>,
37    /// The will message
38    will_message: Option<Bytes>,
39    /// The username
40    username: Option<Bytes>,
41    /// The password
42    password: Option<Bytes>,
43}
44impl<Bytes> Connect<Bytes>
45where
46    Bytes: AnyVec<u8>,
47{
48    /// The packet type constant
49    pub const TYPE: u8 = 1;
50
51    /// The protocol name
52    const PROTOCOL_NAME: [u8; 6] = *b"\x00\x04MQTT";
53    /// The protocol constant for MQTT 3.1.1
54    const PROTOCOL_LEVEL_MQTT_3_1_1: u8 = 0x04;
55
56    /// Creates a new packet
57    pub fn new<T>(keep_alive_secs: u16, clean_session: bool, client_id: T) -> Result<Self, MemoryError>
58    where
59        T: AsRef<[u8]>,
60    {
61        let client_id = Bytes::new(client_id.as_ref())?;
62        Ok(Self {
63            keep_alive_secs,
64            clean_session,
65            will_retain: false,
66            will_qos: 0,
67            client_id,
68            will_topic: None,
69            will_message: None,
70            username: None,
71            password: None,
72        })
73    }
74    /// Configures a last-will topic and message
75    ///
76    /// # QoS Levels
77    /// Valid QoS levels are:
78    ///  - `0`: At most one delivery
79    ///  - `1`: At least one delivery
80    ///  - `2`: Exactly one delivery
81    pub fn with_will<T, M>(mut self, topic: T, message: M, qos: u8, retain: bool) -> Result<Self, MemoryError>
82    where
83        T: AsRef<[u8]>,
84        M: AsRef<[u8]>,
85    {
86        self.will_topic = Bytes::new(topic.as_ref()).map(Some)?;
87        self.will_message = Bytes::new(message.as_ref()).map(Some)?;
88        self.will_retain = retain;
89        self.will_qos = qos;
90        Ok(self)
91    }
92    /// Configures a username and password
93    pub fn with_username_password<U, P>(mut self, username: U, password: P) -> Result<Self, MemoryError>
94    where
95        U: AsRef<[u8]>,
96        P: AsRef<[u8]>,
97    {
98        self.username = Bytes::new(username.as_ref()).map(Some)?;
99        self.password = Bytes::new(password.as_ref()).map(Some)?;
100        Ok(self)
101    }
102
103    /// Gets the seconds to keep the connection alive
104    pub const fn keep_alive_secs(&self) -> u16 {
105        self.keep_alive_secs
106    }
107
108    /// Gets the clean session bit which indicate if the client and server do not need to process the deletion of state
109    /// atomically
110    pub const fn clean_session(&self) -> bool {
111        self.clean_session
112    }
113
114    /// Gets the client identifier
115    ///
116    /// # Important
117    /// MQTT allows only the characters `0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`, and the
118    /// identifier should not be longer than 23 bytes.
119    pub fn client_id(&self) -> &[u8] {
120        self.client_id.as_ref()
121    }
122
123    /// Gets the will-retain bit to indicate if the will message is to be Retained when it is published
124    pub const fn will_retain(&self) -> bool {
125        self.will_retain
126    }
127    /// Gets the QoS level to be used when publishing the will message
128    pub const fn will_qos(&self) -> u8 {
129        self.will_qos
130    }
131    /// Gets the will topic
132    pub fn will_topic(&self) -> Option<&[u8]> {
133        self.will_topic.as_ref().map(|bytes| bytes.as_ref())
134    }
135    /// Gets the will message
136    pub fn will_message(&self) -> Option<&[u8]> {
137        self.will_message.as_ref().map(|bytes| bytes.as_ref())
138    }
139
140    /// Gets the username
141    pub fn username(&self) -> Option<&[u8]> {
142        self.username.as_ref().map(|bytes| bytes.as_ref())
143    }
144    /// Gets the password
145    pub fn password(&self) -> Option<&[u8]> {
146        self.password.as_ref().map(|bytes| bytes.as_ref())
147    }
148}
149impl<Bytes> TryFromIterator for Connect<Bytes>
150where
151    Bytes: AnyVec<u8>,
152{
153    fn try_from_iter<T>(iter: T) -> Result<Self, DecoderError>
154    where
155        T: IntoIterator<Item = u8>,
156    {
157        // Read packet:
158        //  - header type and `0` flags
159        //  - packet len
160        //  - protocol name
161        //  - protocol level
162        //  - connect flags
163        //  - keep-alive
164        //  - client id
165        //  - will topic
166        //  - will message
167        //  - username
168        //  - password
169        let mut decoder = Decoder::new(iter);
170        let (Self::TYPE, _flags) = decoder.header()? else {
171            return Err(err!(Data::SpecViolation, "invalid packet type"))?;
172        };
173
174        // Limit length
175        let len = decoder.packetlen()?;
176        let mut decoder = decoder.limit(len);
177
178        // Read protocol name byte-by-byte and version
179        let Self::PROTOCOL_NAME = decoder.raw()? else {
180            return Err(err!(Data::SpecViolation, "invalid protocol name"))?;
181        };
182        let Self::PROTOCOL_LEVEL_MQTT_3_1_1 = decoder.u8()? else {
183            return Err(err!(Data::SpecViolation, "invalid protocol version"))?;
184        };
185
186        // Read fields
187        let [f_user, f_pass, will_retain, will_qos0, will_qos1, f_will, clean_session, _] = decoder.bitmap()?;
188        let keep_alive_secs = decoder.u16()?;
189        let client_id = decoder.bytes()?;
190        let will_topic = decoder.optional_bytes(f_will)?;
191        let will_message = decoder.optional_bytes(f_will)?;
192        let username = decoder.optional_bytes(f_user)?;
193        let password = decoder.optional_bytes(f_pass)?;
194
195        // Init self
196        let will_qos = ((will_qos0 as u8) << 1) | (will_qos1 as u8);
197        Ok(Self {
198            keep_alive_secs,
199            clean_session,
200            will_retain,
201            will_qos,
202            client_id,
203            will_topic,
204            will_message,
205            username,
206            password,
207        })
208    }
209}
210impl<Bytes> IntoIterator for Connect<Bytes>
211where
212    Bytes: AnyVec<u8>,
213{
214    type Item = u8;
215    #[rustfmt::skip]
216    type IntoIter =
217        // Complex iterator built out of the individual message fields
218        Chain<Chain<Chain<Chain<Chain<Chain<Chain<Chain<Chain<Chain<Chain<
219            // - header type and `0` flags
220            Unit, U8Iter>,
221            // - packet len
222            PacketLenIter>,
223            // - protocol name
224            <[u8; 6] as IntoIterator>::IntoIter>,
225            // - protocol level
226            U8Iter>,
227            // - connect flags
228            U8Iter>,
229            // - keep-alive
230            U16Iter>,
231            // - client id
232            BytesIter<Bytes>>,
233            // - will topic
234            OptionalBytesIter<Bytes>>,
235            // - will message
236            OptionalBytesIter<Bytes>>,
237            // - username
238            OptionalBytesIter<Bytes>>,
239            // - password
240            OptionalBytesIter<Bytes>>;
241
242    fn into_iter(self) -> Self::IntoIter {
243        // Assemble protocol name and flags
244        let flags = [
245            self.username.is_some(),
246            self.password.is_some(),
247            self.will_retain,
248            (self.will_qos >> 1) != 0,
249            (self.will_qos & 1) != 0,
250            self.will_topic.is_some(),
251            self.clean_session,
252            false,
253        ];
254
255        // Precompute body length:
256        //  - protocol name
257        //  - protocol level
258        //  - connect flags
259        //  - keep-alive
260        //  - client id
261        //  - will topic
262        //  - will message
263        //  - username
264        //  - password
265        let len = Length::new()
266            .raw(&Self::PROTOCOL_NAME)
267            .u8(&Self::PROTOCOL_LEVEL_MQTT_3_1_1)
268            .bitmap(&flags)
269            .u16(&self.keep_alive_secs)
270            .bytes(&self.client_id)
271            .optional_bytes(&self.will_topic)
272            .optional_bytes(&self.will_message)
273            .optional_bytes(&self.username)
274            .optional_bytes(&self.password)
275            .into();
276
277        // Write header:
278        //  - header type and `0` flags
279        //  - packet len
280        //  - protocol name
281        //  - protocol level
282        //  - connect flags
283        //  - keep-alive
284        //  - client id
285        //  - will topic
286        //  - will message
287        //  - username
288        //  - password
289        Encoder::default()
290            .header(Self::TYPE, [false, false, false, false])
291            .packetlen(len)
292            .raw(Self::PROTOCOL_NAME)
293            .u8(Self::PROTOCOL_LEVEL_MQTT_3_1_1)
294            .bitmap(flags)
295            .u16(self.keep_alive_secs)
296            .bytes(self.client_id)
297            .optional_bytes(self.will_topic)
298            .optional_bytes(self.will_message)
299            .optional_bytes(self.username)
300            .optional_bytes(self.password)
301            .into_iter()
302    }
303}