mqttbytes/v5/
pubrel.rs

1use super::*;
2use bytes::{Buf, BufMut, Bytes, BytesMut};
3
4/// Return code in connack
5#[derive(Debug, Clone, Copy, PartialEq)]
6#[repr(u8)]
7pub enum PubRelReason {
8    Success = 0,
9    PacketIdentifierNotFound = 146,
10}
11
12/// Acknowledgement to QoS1 publish
13#[derive(Debug, Clone, PartialEq)]
14pub struct PubRel {
15    pub pkid: u16,
16    pub reason: PubRelReason,
17    pub properties: Option<PubRelProperties>,
18}
19
20impl PubRel {
21    pub fn new(pkid: u16) -> PubRel {
22        PubRel {
23            pkid,
24            reason: PubRelReason::Success,
25            properties: None,
26        }
27    }
28
29    fn len(&self) -> usize {
30        let mut len = 2 + 1; // pkid + reason
31
32        // If there are no properties during success, sending reason code is optional
33        if self.reason == PubRelReason::Success && self.properties.is_none() {
34            return 2;
35        }
36
37        if let Some(properties) = &self.properties {
38            let properties_len = properties.len();
39            let properties_len_len = len_len(properties_len);
40            len += properties_len_len + properties_len;
41        }
42
43        len
44    }
45
46    pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result<Self, Error> {
47        let variable_header_index = fixed_header.fixed_header_len;
48        bytes.advance(variable_header_index);
49        let pkid = read_u16(&mut bytes)?;
50        if fixed_header.remaining_len == 2 {
51            return Ok(PubRel {
52                pkid,
53                reason: PubRelReason::Success,
54                properties: None,
55            });
56        }
57
58        let ack_reason = read_u8(&mut bytes)?;
59        if fixed_header.remaining_len < 4 {
60            return Ok(PubRel {
61                pkid,
62                reason: reason(ack_reason)?,
63                properties: None,
64            });
65        }
66
67        let puback = PubRel {
68            pkid,
69            reason: reason(ack_reason)?,
70            properties: PubRelProperties::extract(&mut bytes)?,
71        };
72
73        Ok(puback)
74    }
75
76    pub fn write(&self, buffer: &mut BytesMut) -> Result<usize, Error> {
77        let len = self.len();
78        buffer.put_u8(0x62);
79        let count = write_remaining_length(buffer, len)?;
80        buffer.put_u16(self.pkid);
81
82        // If there are no properties during success, sending reason code is optional
83        if self.reason == PubRelReason::Success && self.properties.is_none() {
84            return Ok(4);
85        }
86
87        buffer.put_u8(self.reason as u8);
88
89        if let Some(properties) = &self.properties {
90            properties.write(buffer)?;
91        }
92
93        Ok(1 + count + len)
94    }
95}
96
97#[derive(Debug, Clone, PartialEq)]
98pub struct PubRelProperties {
99    pub reason_string: Option<String>,
100    pub user_properties: Vec<(String, String)>,
101}
102
103impl PubRelProperties {
104    pub fn len(&self) -> usize {
105        let mut len = 0;
106
107        if let Some(reason) = &self.reason_string {
108            len += 1 + 2 + reason.len();
109        }
110
111        for (key, value) in self.user_properties.iter() {
112            len += 1 + 2 + key.len() + 2 + value.len();
113        }
114
115        len
116    }
117
118    pub fn extract(mut bytes: &mut Bytes) -> Result<Option<PubRelProperties>, Error> {
119        let mut reason_string = None;
120        let mut user_properties = Vec::new();
121
122        let (properties_len_len, properties_len) = length(bytes.iter())?;
123        bytes.advance(properties_len_len);
124        if properties_len == 0 {
125            return Ok(None);
126        }
127
128        let mut cursor = 0;
129        // read until cursor reaches property length. properties_len = 0 will skip this loop
130        while cursor < properties_len {
131            let prop = read_u8(&mut bytes)?;
132            cursor += 1;
133
134            match property(prop)? {
135                PropertyType::ReasonString => {
136                    let reason = read_mqtt_string(&mut bytes)?;
137                    cursor += 2 + reason.len();
138                    reason_string = Some(reason);
139                }
140                PropertyType::UserProperty => {
141                    let key = read_mqtt_string(&mut bytes)?;
142                    let value = read_mqtt_string(&mut bytes)?;
143                    cursor += 2 + key.len() + 2 + value.len();
144                    user_properties.push((key, value));
145                }
146                _ => return Err(Error::InvalidPropertyType(prop)),
147            }
148        }
149
150        Ok(Some(PubRelProperties {
151            reason_string,
152            user_properties,
153        }))
154    }
155
156    fn write(&self, buffer: &mut BytesMut) -> Result<(), Error> {
157        let len = self.len();
158        write_remaining_length(buffer, len)?;
159
160        if let Some(reason) = &self.reason_string {
161            buffer.put_u8(PropertyType::ReasonString as u8);
162            write_mqtt_string(buffer, reason);
163        }
164
165        for (key, value) in self.user_properties.iter() {
166            buffer.put_u8(PropertyType::UserProperty as u8);
167            write_mqtt_string(buffer, key);
168            write_mqtt_string(buffer, value);
169        }
170
171        Ok(())
172    }
173}
174/// Connection return code type
175fn reason(num: u8) -> Result<PubRelReason, Error> {
176    let code = match num {
177        0 => PubRelReason::Success,
178        146 => PubRelReason::PacketIdentifierNotFound,
179        num => return Err(Error::InvalidConnectReturnCode(num)),
180    };
181
182    Ok(code)
183}
184
185#[cfg(test)]
186mod test {
187    use super::*;
188    use alloc::vec;
189    use bytes::BytesMut;
190    use pretty_assertions::assert_eq;
191
192    fn sample() -> PubRel {
193        let properties = PubRelProperties {
194            reason_string: Some("test".to_owned()),
195            user_properties: vec![("test".to_owned(), "test".to_owned())],
196        };
197
198        PubRel {
199            pkid: 42,
200            reason: PubRelReason::PacketIdentifierNotFound,
201            properties: Some(properties),
202        }
203    }
204
205    fn sample_bytes() -> Vec<u8> {
206        vec![
207            0x62, // payload type
208            0x18, // remaining length
209            0x00, 0x2a, // packet id
210            0x92, // reason
211            0x14, // properties len
212            0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // reason_string
213            0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
214            0x74, // user properties
215        ]
216    }
217
218    #[test]
219    fn pubrel_parsing_works() {
220        let mut stream = bytes::BytesMut::new();
221        let packetstream = &sample_bytes();
222        stream.extend_from_slice(&packetstream[..]);
223
224        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
225        let pubrel_bytes = stream.split_to(fixed_header.frame_length()).freeze();
226        let pubrel = PubRel::read(fixed_header, pubrel_bytes).unwrap();
227        assert_eq!(pubrel, sample());
228    }
229
230    #[test]
231    fn pubrel_encoding_works() {
232        let pubrel = sample();
233        let mut buf = BytesMut::new();
234        pubrel.write(&mut buf).unwrap();
235        assert_eq!(&buf[..], sample_bytes());
236    }
237}