1use super::{
2 Error, FixedHeader, PropertyType, len_len, length, property, read_mqtt_string, read_u8,
3 read_u16, write_mqtt_string, write_remaining_length,
4};
5use bytes::{Buf, BufMut, Bytes, BytesMut};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[repr(u8)]
10pub enum PubRelReason {
11 Success = 0,
12 PacketIdentifierNotFound = 146,
13}
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct PubRel {
18 pub pkid: u16,
19 pub reason: PubRelReason,
20 pub properties: Option<PubRelProperties>,
21}
22
23impl PubRel {
24 #[must_use]
25 pub const fn new(pkid: u16, properties: Option<PubRelProperties>) -> Self {
26 Self {
27 pkid,
28 reason: PubRelReason::Success,
29 properties,
30 }
31 }
32
33 #[must_use]
34 pub fn size(&self) -> usize {
35 if self.reason == PubRelReason::Success && self.properties.is_none() {
37 return 4;
38 }
39
40 let len = self.len();
41 let remaining_len_size = len_len(len);
42
43 1 + remaining_len_size + len
44 }
45
46 fn len(&self) -> usize {
47 let mut len = 2 + 1; if self.reason == PubRelReason::Success && self.properties.is_none() {
53 return 2;
54 }
55
56 if let Some(p) = &self.properties {
57 let properties_len = p.len();
58 let properties_len_len = len_len(properties_len);
59 len += properties_len_len + properties_len;
60 } else {
61 len += 1;
62 }
63
64 len
65 }
66
67 pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result<Self, Error> {
68 let variable_header_index = fixed_header.header_len;
69 bytes.advance(variable_header_index);
70 let pkid = read_u16(&mut bytes)?;
71 if fixed_header.remaining_len == 2 {
72 return Ok(Self {
73 pkid,
74 reason: PubRelReason::Success,
75 properties: None,
76 });
77 }
78
79 let ack_reason = read_u8(&mut bytes)?;
80 if fixed_header.remaining_len < 4 {
81 return Ok(Self {
82 pkid,
83 reason: reason(ack_reason)?,
84 properties: None,
85 });
86 }
87
88 let properties = PubRelProperties::read(&mut bytes)?;
89 let puback = Self {
90 pkid,
91 reason: reason(ack_reason)?,
92 properties,
93 };
94
95 Ok(puback)
96 }
97
98 pub fn write(&self, buffer: &mut BytesMut) -> Result<usize, Error> {
99 let len = self.len();
100 buffer.put_u8(0x62);
101 let count = write_remaining_length(buffer, len)?;
102 buffer.put_u16(self.pkid);
103
104 if self.reason == PubRelReason::Success && self.properties.is_none() {
106 return Ok(4);
107 }
108
109 buffer.put_u8(code(self.reason));
110
111 if let Some(p) = &self.properties {
112 p.write(buffer)?;
113 } else {
114 write_remaining_length(buffer, 0)?;
115 }
116
117 Ok(1 + count + len)
118 }
119}
120
121#[derive(Debug, Clone, PartialEq, Eq)]
122pub struct PubRelProperties {
123 pub reason_string: Option<String>,
124 pub user_properties: Vec<(String, String)>,
125}
126
127impl PubRelProperties {
128 fn len(&self) -> usize {
129 let mut len = 0;
130
131 if let Some(reason) = &self.reason_string {
132 len += 1 + 2 + reason.len();
133 }
134
135 for (key, value) in &self.user_properties {
136 len += 1 + 2 + key.len() + 2 + value.len();
137 }
138
139 len
140 }
141
142 pub fn read(bytes: &mut Bytes) -> Result<Option<Self>, Error> {
143 let mut reason_string = None;
144 let mut user_properties = Vec::new();
145
146 let (properties_len_len, properties_len) = length(bytes.iter())?;
147 bytes.advance(properties_len_len);
148 if properties_len == 0 {
149 return Ok(None);
150 }
151
152 let mut cursor = 0;
153 while cursor < properties_len {
155 let prop = read_u8(bytes)?;
156 cursor += 1;
157
158 match property(prop)? {
159 PropertyType::ReasonString => {
160 let reason = read_mqtt_string(bytes)?;
161 cursor += 2 + reason.len();
162 reason_string = Some(reason);
163 }
164 PropertyType::UserProperty => {
165 let key = read_mqtt_string(bytes)?;
166 let value = read_mqtt_string(bytes)?;
167 cursor += 2 + key.len() + 2 + value.len();
168 user_properties.push((key, value));
169 }
170 _ => return Err(Error::InvalidPropertyType(prop)),
171 }
172 }
173
174 Ok(Some(Self {
175 reason_string,
176 user_properties,
177 }))
178 }
179
180 pub fn write(&self, buffer: &mut BytesMut) -> Result<(), Error> {
181 let len = self.len();
182 write_remaining_length(buffer, len)?;
183
184 if let Some(reason) = &self.reason_string {
185 buffer.put_u8(PropertyType::ReasonString as u8);
186 write_mqtt_string(buffer, reason);
187 }
188
189 for (key, value) in &self.user_properties {
190 buffer.put_u8(PropertyType::UserProperty as u8);
191 write_mqtt_string(buffer, key);
192 write_mqtt_string(buffer, value);
193 }
194
195 Ok(())
196 }
197}
198
199fn reason(num: u8) -> Result<PubRelReason, Error> {
201 num.try_into()
202}
203
204const fn code(reason: PubRelReason) -> u8 {
205 reason as u8
206}
207
208impl TryFrom<u8> for PubRelReason {
209 type Error = Error;
210
211 fn try_from(value: u8) -> Result<Self, Self::Error> {
212 match value {
213 0 => Ok(Self::Success),
214 146 => Ok(Self::PacketIdentifierNotFound),
215 _ => Err(Error::InvalidConnectReturnCode(value)),
216 }
217 }
218}
219
220#[cfg(test)]
221mod test {
222 use super::super::test::{USER_PROP_KEY, USER_PROP_VAL};
223 use super::*;
224 use bytes::BytesMut;
225 use pretty_assertions::assert_eq;
226
227 #[test]
228 fn length_calculation() {
229 let mut dummy_bytes = BytesMut::new();
230 let pubrel_props = PubRelProperties {
233 reason_string: None,
234 user_properties: vec![(USER_PROP_KEY.into(), USER_PROP_VAL.into())],
235 };
236
237 let pubrel_pkt = PubRel::new(1, Some(pubrel_props));
238
239 let size_from_size = pubrel_pkt.size();
240 let size_from_write = pubrel_pkt.write(&mut dummy_bytes).unwrap();
241 let size_from_bytes = dummy_bytes.len();
242
243 assert_eq!(size_from_write, size_from_bytes);
244 assert_eq!(size_from_size, size_from_bytes);
245 }
246
247 #[test]
248 fn reason_code_cast_matches_spec() {
249 assert_eq!(PubRelReason::Success as u8, 0);
250 assert_eq!(PubRelReason::PacketIdentifierNotFound as u8, 146);
251 }
252
253 #[test]
254 fn reason_and_code_round_trip() {
255 let values = [
256 (0, PubRelReason::Success),
257 (146, PubRelReason::PacketIdentifierNotFound),
258 ];
259
260 for (raw, parsed) in values {
261 assert_eq!(reason(raw).unwrap(), parsed);
262 assert_eq!(code(parsed), raw);
263 }
264 }
265
266 #[test]
267 fn reason_invalid_code_errors() {
268 assert!(matches!(
269 reason(42),
270 Err(Error::InvalidConnectReturnCode(42))
271 ));
272 }
273}