mqtt_bytes_v5/
pubrel.rs

1use crate::MqttString;
2
3use super::{
4    len_len, length, property, read_mqtt_string, read_u16, read_u8, write_mqtt_string,
5    write_remaining_length, Debug, Error, FixedHeader, PropertyType,
6};
7use bytes::{Buf, BufMut, Bytes, BytesMut};
8
9/// Return code in `PubRel`
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11#[repr(u8)]
12pub enum PubRelReason {
13    Success,
14    PacketIdentifierNotFound,
15}
16
17/// `QoS2` Publish release, in response to PUBREC packet
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct PubRel {
20    pub pkid: u16,
21    pub reason: PubRelReason,
22    pub properties: Option<PubRelProperties>,
23}
24
25impl PubRel {
26    #[must_use]
27    pub fn new(pkid: u16, properties: Option<PubRelProperties>) -> Self {
28        Self {
29            pkid,
30            reason: PubRelReason::Success,
31            properties,
32        }
33    }
34
35    #[must_use]
36    pub fn size(&self) -> usize {
37        // If there are no properties during success, sending reason code is optional
38        if self.reason == PubRelReason::Success && self.properties.is_none() {
39            return 4;
40        }
41
42        let len = self.len();
43        let remaining_len_size = len_len(len);
44
45        1 + remaining_len_size + len
46    }
47
48    fn len(&self) -> usize {
49        let mut len = 2 + 1; // pkid + reason
50
51        // The Reason Code and Property Length can be omitted if the Reason Code is 0x00 (Success)
52        // and there are no Properties. In this case the PUBREL has a Remaining Length of 2.
53        // <https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901144>
54        if self.reason == PubRelReason::Success && self.properties.is_none() {
55            return 2;
56        }
57
58        if let Some(p) = &self.properties {
59            let properties_len = p.len();
60            let properties_len_len = len_len(properties_len);
61            len += properties_len_len + properties_len;
62        } else {
63            len += 1;
64        }
65
66        len
67    }
68
69    pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result<PubRel, Error> {
70        let variable_header_index = fixed_header.fixed_header_len;
71        bytes.advance(variable_header_index);
72        let pkid = read_u16(&mut bytes)?;
73        if fixed_header.remaining_len == 2 {
74            return Ok(PubRel {
75                pkid,
76                reason: PubRelReason::Success,
77                properties: None,
78            });
79        }
80
81        let ack_reason = read_u8(&mut bytes)?;
82        if fixed_header.remaining_len < 4 {
83            return Ok(PubRel {
84                pkid,
85                reason: reason(ack_reason)?,
86                properties: None,
87            });
88        }
89
90        let properties = PubRelProperties::read(&mut bytes)?;
91        let puback = PubRel {
92            pkid,
93            reason: reason(ack_reason)?,
94            properties,
95        };
96
97        Ok(puback)
98    }
99
100    pub fn write(&self, buffer: &mut BytesMut) -> Result<usize, Error> {
101        let len = self.len();
102        buffer.put_u8(0x62);
103        let count = write_remaining_length(buffer, len)?;
104        buffer.put_u16(self.pkid);
105
106        // If there are no properties during success, sending reason code is optional
107        if self.reason == PubRelReason::Success && self.properties.is_none() {
108            return Ok(4);
109        }
110
111        buffer.put_u8(code(self.reason));
112
113        if let Some(p) = &self.properties {
114            p.write(buffer)?;
115        } else {
116            write_remaining_length(buffer, 0)?;
117        }
118
119        Ok(1 + count + len)
120    }
121}
122
123#[derive(Debug, Clone, PartialEq, Eq)]
124pub struct PubRelProperties {
125    pub reason_string: Option<MqttString>,
126    pub user_properties: Vec<(MqttString, MqttString)>,
127}
128
129impl PubRelProperties {
130    fn len(&self) -> usize {
131        let mut len = 0;
132
133        if let Some(reason) = &self.reason_string {
134            len += 1 + 2 + reason.len();
135        }
136
137        for (key, value) in &self.user_properties {
138            len += 1 + 2 + key.len() + 2 + value.len();
139        }
140
141        len
142    }
143
144    pub fn read(bytes: &mut Bytes) -> Result<Option<PubRelProperties>, Error> {
145        let mut reason_string = None;
146        let mut user_properties = Vec::new();
147
148        let (properties_len_len, properties_len) = length(bytes.iter())?;
149        bytes.advance(properties_len_len);
150        if properties_len == 0 {
151            return Ok(None);
152        }
153
154        let mut cursor = 0;
155        // read until cursor reaches property length. properties_len = 0 will skip this loop
156        while cursor < properties_len {
157            let prop = read_u8(bytes)?;
158            cursor += 1;
159
160            match property(prop)? {
161                PropertyType::ReasonString => {
162                    let reason = read_mqtt_string(bytes)?;
163                    cursor += 2 + reason.len();
164                    reason_string = Some(reason);
165                }
166                PropertyType::UserProperty => {
167                    let key = read_mqtt_string(bytes)?;
168                    let value = read_mqtt_string(bytes)?;
169                    cursor += 2 + key.len() + 2 + value.len();
170                    user_properties.push((key, value));
171                }
172                _ => return Err(Error::InvalidPropertyType(prop)),
173            }
174        }
175
176        Ok(Some(PubRelProperties {
177            reason_string,
178            user_properties,
179        }))
180    }
181
182    pub fn write(&self, buffer: &mut BytesMut) -> Result<(), Error> {
183        let len = self.len();
184        write_remaining_length(buffer, len)?;
185
186        if let Some(reason) = &self.reason_string {
187            buffer.put_u8(PropertyType::ReasonString as u8);
188            write_mqtt_string(buffer, reason)?;
189        }
190
191        for (key, value) in &self.user_properties {
192            buffer.put_u8(PropertyType::UserProperty as u8);
193            write_mqtt_string(buffer, key)?;
194            write_mqtt_string(buffer, value)?;
195        }
196
197        Ok(())
198    }
199}
200
201/// Connection return code type
202fn reason(num: u8) -> Result<PubRelReason, Error> {
203    let code = match num {
204        0 => PubRelReason::Success,
205        146 => PubRelReason::PacketIdentifierNotFound,
206        num => return Err(Error::InvalidConnectReturnCode(num)),
207    };
208
209    Ok(code)
210}
211
212fn code(reason: PubRelReason) -> u8 {
213    match reason {
214        PubRelReason::Success => 0,
215        PubRelReason::PacketIdentifierNotFound => 146,
216    }
217}
218
219#[cfg(test)]
220mod test {
221    use crate::test::read_write_packets;
222    use crate::Packet;
223
224    use super::super::test::{USER_PROP_KEY, USER_PROP_VAL};
225    use super::*;
226    use bytes::BytesMut;
227    use pretty_assertions::assert_eq;
228
229    #[test]
230    fn length_calculation() {
231        let mut dummy_bytes = BytesMut::new();
232        // Use user_properties to pad the size to exceed ~128 bytes to make the
233        // remaining_length field in the packet be 2 bytes long.
234        let pubrel_props = PubRelProperties {
235            reason_string: None,
236            user_properties: vec![(USER_PROP_KEY.into(), USER_PROP_VAL.into())],
237        };
238
239        let pubrel_pkt = PubRel::new(1, Some(pubrel_props));
240
241        let size_from_size = pubrel_pkt.size();
242        let size_from_write = pubrel_pkt.write(&mut dummy_bytes).unwrap();
243        let size_from_bytes = dummy_bytes.len();
244
245        assert_eq!(size_from_write, size_from_bytes);
246        assert_eq!(size_from_size, size_from_bytes);
247    }
248
249    #[test]
250    fn test_write_read() {
251        read_write_packets(write_read_provider());
252    }
253
254    fn write_read_provider() -> Vec<Packet> {
255        vec![
256            Packet::PubRel(PubRel {
257                pkid: 42,
258                reason: PubRelReason::Success,
259                properties: None,
260            }),
261            Packet::PubRel(PubRel {
262                pkid: 42,
263                reason: PubRelReason::Success,
264                properties: Some(PubRelProperties {
265                    reason_string: Some("hello".into()),
266                    user_properties: vec![(USER_PROP_KEY.into(), USER_PROP_VAL.into())],
267                }),
268            }),
269        ]
270    }
271}