1use crate::error::{MqttError, Result};
2use crate::packet::{FixedHeader, MqttPacket, PacketType};
3use crate::protocol::v5::properties::Properties;
4use crate::types::ProtocolVersion;
5use crate::QoS;
6use bytes::{Buf, BufMut};
7
8#[derive(Debug, Clone)]
10pub struct SubAckPacket {
11 pub packet_id: u16,
13 pub reason_codes: Vec<SubAckReasonCode>,
15 pub properties: Properties,
17 pub protocol_version: u8,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23#[repr(u8)]
24pub enum SubAckReasonCode {
25 GrantedQoS0 = 0x00,
27 GrantedQoS1 = 0x01,
29 GrantedQoS2 = 0x02,
31 UnspecifiedError = 0x80,
33 ImplementationSpecificError = 0x83,
35 NotAuthorized = 0x87,
37 TopicFilterInvalid = 0x8F,
39 PacketIdentifierInUse = 0x91,
41 QuotaExceeded = 0x97,
43 SharedSubscriptionsNotSupported = 0x9E,
45 SubscriptionIdentifiersNotSupported = 0xA1,
47 WildcardSubscriptionsNotSupported = 0xA2,
49}
50
51impl SubAckReasonCode {
52 #[must_use]
54 pub fn from_qos(qos: QoS) -> Self {
55 match qos {
56 QoS::AtMostOnce => Self::GrantedQoS0,
57 QoS::AtLeastOnce => Self::GrantedQoS1,
58 QoS::ExactlyOnce => Self::GrantedQoS2,
59 }
60 }
61
62 #[must_use]
64 pub fn from_u8(value: u8) -> Option<Self> {
65 match value {
66 0x00 => Some(Self::GrantedQoS0),
67 0x01 => Some(Self::GrantedQoS1),
68 0x02 => Some(Self::GrantedQoS2),
69 0x80 => Some(Self::UnspecifiedError),
70 0x83 => Some(Self::ImplementationSpecificError),
71 0x87 => Some(Self::NotAuthorized),
72 0x8F => Some(Self::TopicFilterInvalid),
73 0x91 => Some(Self::PacketIdentifierInUse),
74 0x97 => Some(Self::QuotaExceeded),
75 0x9E => Some(Self::SharedSubscriptionsNotSupported),
76 0xA1 => Some(Self::SubscriptionIdentifiersNotSupported),
77 0xA2 => Some(Self::WildcardSubscriptionsNotSupported),
78 _ => None,
79 }
80 }
81
82 #[must_use]
84 pub fn is_success(&self) -> bool {
85 matches!(
86 self,
87 Self::GrantedQoS0 | Self::GrantedQoS1 | Self::GrantedQoS2
88 )
89 }
90
91 #[must_use]
93 pub fn granted_qos(&self) -> Option<QoS> {
94 match self {
95 Self::GrantedQoS0 => Some(QoS::AtMostOnce),
96 Self::GrantedQoS1 => Some(QoS::AtLeastOnce),
97 Self::GrantedQoS2 => Some(QoS::ExactlyOnce),
98 _ => None,
99 }
100 }
101}
102
103impl SubAckPacket {
104 #[must_use]
106 pub fn new(packet_id: u16) -> Self {
107 Self {
108 packet_id,
109 reason_codes: Vec::new(),
110 properties: Properties::default(),
111 protocol_version: 5,
112 }
113 }
114
115 #[must_use]
117 pub fn new_v311(packet_id: u16) -> Self {
118 Self {
119 packet_id,
120 reason_codes: Vec::new(),
121 properties: Properties::default(),
122 protocol_version: 4,
123 }
124 }
125
126 fn reason_code_to_v311(code: SubAckReasonCode) -> u8 {
127 match code {
128 SubAckReasonCode::GrantedQoS0 => 0x00,
129 SubAckReasonCode::GrantedQoS1 => 0x01,
130 SubAckReasonCode::GrantedQoS2 => 0x02,
131 _ => 0x80,
132 }
133 }
134
135 #[must_use]
137 pub fn add_reason_code(mut self, code: SubAckReasonCode) -> Self {
138 self.reason_codes.push(code);
139 self
140 }
141
142 #[must_use]
144 pub fn add_granted_qos(mut self, qos: QoS) -> Self {
145 self.reason_codes.push(SubAckReasonCode::from_qos(qos));
146 self
147 }
148
149 #[must_use]
151 pub fn with_reason_string(mut self, reason: String) -> Self {
152 self.properties.set_reason_string(reason);
153 self
154 }
155
156 #[must_use]
158 pub fn with_user_property(mut self, key: String, value: String) -> Self {
159 self.properties.add_user_property(key, value);
160 self
161 }
162}
163
164impl MqttPacket for SubAckPacket {
165 fn packet_type(&self) -> PacketType {
166 PacketType::SubAck
167 }
168
169 fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
170 buf.put_u16(self.packet_id);
171
172 if self.protocol_version == 5 {
173 self.properties.encode(buf)?;
174 }
175
176 if self.reason_codes.is_empty() {
177 return Err(MqttError::MalformedPacket(
178 "SUBACK packet must contain at least one reason code".to_string(),
179 ));
180 }
181
182 if self.protocol_version == 5 {
183 for code in &self.reason_codes {
184 buf.put_u8(*code as u8);
185 }
186 } else {
187 for code in &self.reason_codes {
188 buf.put_u8(Self::reason_code_to_v311(*code));
189 }
190 }
191
192 Ok(())
193 }
194
195 fn decode_body<B: Buf>(buf: &mut B, fixed_header: &FixedHeader) -> Result<Self> {
196 Self::decode_body_with_version(buf, fixed_header, 5)
197 }
198}
199
200impl SubAckPacket {
201 pub fn decode_body_with_version<B: Buf>(
207 buf: &mut B,
208 _fixed_header: &FixedHeader,
209 protocol_version: u8,
210 ) -> Result<Self> {
211 ProtocolVersion::try_from(protocol_version)
212 .map_err(|()| MqttError::UnsupportedProtocolVersion)?;
213
214 if buf.remaining() < 2 {
215 return Err(MqttError::MalformedPacket(
216 "SUBACK missing packet identifier".to_string(),
217 ));
218 }
219 let packet_id = buf.get_u16();
220
221 let properties = if protocol_version == 5 {
222 Properties::decode(buf)?
223 } else {
224 Properties::default()
225 };
226
227 let mut reason_codes = Vec::new();
228
229 if !buf.has_remaining() {
230 return Err(MqttError::MalformedPacket(
231 "SUBACK packet must contain at least one reason code".to_string(),
232 ));
233 }
234
235 while buf.has_remaining() {
236 let code_byte = buf.get_u8();
237 let code = SubAckReasonCode::from_u8(code_byte).ok_or_else(|| {
238 MqttError::MalformedPacket(format!("Invalid SUBACK reason code: 0x{code_byte:02X}"))
239 })?;
240 reason_codes.push(code);
241 }
242
243 Ok(Self {
244 packet_id,
245 reason_codes,
246 properties,
247 protocol_version,
248 })
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255 use crate::protocol::v5::properties::PropertyId;
256 use bytes::BytesMut;
257
258 #[test]
259 fn test_suback_reason_code_from_qos() {
260 assert_eq!(
261 SubAckReasonCode::from_qos(QoS::AtMostOnce),
262 SubAckReasonCode::GrantedQoS0
263 );
264 assert_eq!(
265 SubAckReasonCode::from_qos(QoS::AtLeastOnce),
266 SubAckReasonCode::GrantedQoS1
267 );
268 assert_eq!(
269 SubAckReasonCode::from_qos(QoS::ExactlyOnce),
270 SubAckReasonCode::GrantedQoS2
271 );
272 }
273
274 #[test]
275 fn test_suback_reason_code_is_success() {
276 assert!(SubAckReasonCode::GrantedQoS0.is_success());
277 assert!(SubAckReasonCode::GrantedQoS1.is_success());
278 assert!(SubAckReasonCode::GrantedQoS2.is_success());
279 assert!(!SubAckReasonCode::NotAuthorized.is_success());
280 assert!(!SubAckReasonCode::TopicFilterInvalid.is_success());
281 }
282
283 #[test]
284 fn test_suback_basic() {
285 let packet = SubAckPacket::new(123)
286 .add_granted_qos(QoS::AtLeastOnce)
287 .add_granted_qos(QoS::ExactlyOnce)
288 .add_reason_code(SubAckReasonCode::NotAuthorized);
289
290 assert_eq!(packet.packet_id, 123);
291 assert_eq!(packet.reason_codes.len(), 3);
292 assert_eq!(packet.reason_codes[0], SubAckReasonCode::GrantedQoS1);
293 assert_eq!(packet.reason_codes[1], SubAckReasonCode::GrantedQoS2);
294 assert_eq!(packet.reason_codes[2], SubAckReasonCode::NotAuthorized);
295 }
296
297 #[test]
298 fn test_suback_encode_decode() {
299 let packet = SubAckPacket::new(789)
300 .add_granted_qos(QoS::AtMostOnce)
301 .add_granted_qos(QoS::AtLeastOnce)
302 .add_reason_code(SubAckReasonCode::TopicFilterInvalid)
303 .with_reason_string("Invalid wildcard usage".to_string());
304
305 let mut buf = BytesMut::new();
306 packet.encode(&mut buf).unwrap();
307
308 let fixed_header = FixedHeader::decode(&mut buf).unwrap();
309 assert_eq!(fixed_header.packet_type, PacketType::SubAck);
310
311 let decoded = SubAckPacket::decode_body(&mut buf, &fixed_header).unwrap();
312 assert_eq!(decoded.packet_id, 789);
313 assert_eq!(decoded.reason_codes.len(), 3);
314 assert_eq!(decoded.reason_codes[0], SubAckReasonCode::GrantedQoS0);
315 assert_eq!(decoded.reason_codes[1], SubAckReasonCode::GrantedQoS1);
316 assert_eq!(
317 decoded.reason_codes[2],
318 SubAckReasonCode::TopicFilterInvalid
319 );
320
321 let reason_str = decoded.properties.get(PropertyId::ReasonString);
322 assert!(reason_str.is_some());
323 }
324
325 #[test]
326 fn test_suback_empty_reason_codes() {
327 let packet = SubAckPacket::new(123);
328
329 let mut buf = BytesMut::new();
330 let result = packet.encode(&mut buf);
331 assert!(result.is_err());
332 }
333}