mqtt5_protocol/packet/
pubrel.rs1use crate::error::{MqttError, Result};
2use crate::packet::{FixedHeader, MqttPacket, PacketType};
3use crate::protocol::v5::properties::Properties;
4use crate::types::ReasonCode;
5use bytes::{Buf, BufMut};
6
7#[derive(Debug, Clone)]
9pub struct PubRelPacket {
10 pub packet_id: u16,
12 pub reason_code: ReasonCode,
14 pub properties: Properties,
16}
17
18impl PubRelPacket {
19 #[must_use]
21 pub fn new(packet_id: u16) -> Self {
22 Self {
23 packet_id,
24 reason_code: ReasonCode::Success,
25 properties: Properties::default(),
26 }
27 }
28
29 #[must_use]
31 pub fn new_with_reason(packet_id: u16, reason_code: ReasonCode) -> Self {
32 Self {
33 packet_id,
34 reason_code,
35 properties: Properties::default(),
36 }
37 }
38
39 #[must_use]
41 pub fn with_reason_string(mut self, reason: String) -> Self {
42 self.properties.set_reason_string(reason);
43 self
44 }
45
46 #[must_use]
48 pub fn with_user_property(mut self, key: String, value: String) -> Self {
49 self.properties.add_user_property(key, value);
50 self
51 }
52
53 fn is_valid_pubrel_reason_code(code: ReasonCode) -> bool {
55 matches!(
56 code,
57 ReasonCode::Success | ReasonCode::PacketIdentifierNotFound
58 )
59 }
60}
61
62impl MqttPacket for PubRelPacket {
63 fn packet_type(&self) -> PacketType {
64 PacketType::PubRel
65 }
66
67 fn flags(&self) -> u8 {
68 0x02 }
70
71 fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
72 buf.put_u16(self.packet_id);
74
75 if self.reason_code != ReasonCode::Success || !self.properties.is_empty() {
78 buf.put_u8(u8::from(self.reason_code));
79 self.properties.encode(buf)?;
80 }
81
82 Ok(())
83 }
84
85 fn decode_body<B: Buf>(buf: &mut B, fixed_header: &FixedHeader) -> Result<Self> {
86 if fixed_header.flags != 0x02 {
88 return Err(MqttError::MalformedPacket(format!(
89 "Invalid PUBREL flags: expected 0x02, got 0x{:02X}",
90 fixed_header.flags
91 )));
92 }
93
94 if buf.remaining() < 2 {
96 return Err(MqttError::MalformedPacket(
97 "PUBREL missing packet identifier".to_string(),
98 ));
99 }
100 let packet_id = buf.get_u16();
101
102 let (reason_code, properties) = if buf.has_remaining() {
104 let reason_byte = buf.get_u8();
106 let code = ReasonCode::from_u8(reason_byte).ok_or_else(|| {
107 MqttError::MalformedPacket(format!("Invalid PUBREL reason code: {reason_byte}"))
108 })?;
109
110 if !Self::is_valid_pubrel_reason_code(code) {
111 return Err(MqttError::MalformedPacket(format!(
112 "Invalid PUBREL reason code: {code:?}"
113 )));
114 }
115
116 let props = if buf.has_remaining() {
118 Properties::decode(buf)?
119 } else {
120 Properties::default()
121 };
122
123 (code, props)
124 } else {
125 (ReasonCode::Success, Properties::default())
127 };
128
129 Ok(Self {
130 packet_id,
131 reason_code,
132 properties,
133 })
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use crate::protocol::v5::properties::PropertyId;
141 use bytes::BytesMut;
142
143 #[test]
144 fn test_pubrel_basic() {
145 let packet = PubRelPacket::new(123);
146
147 assert_eq!(packet.packet_id, 123);
148 assert_eq!(packet.reason_code, ReasonCode::Success);
149 assert!(packet.properties.is_empty());
150 assert_eq!(packet.flags(), 0x02);
151 }
152
153 #[test]
154 fn test_pubrel_with_reason() {
155 let packet = PubRelPacket::new_with_reason(456, ReasonCode::PacketIdentifierNotFound)
156 .with_reason_string("Packet ID not found".to_string());
157
158 assert_eq!(packet.packet_id, 456);
159 assert_eq!(packet.reason_code, ReasonCode::PacketIdentifierNotFound);
160 assert!(packet.properties.contains(PropertyId::ReasonString));
161 }
162
163 #[test]
164 fn test_pubrel_encode_decode() {
165 let packet = PubRelPacket::new(789);
166
167 let mut buf = BytesMut::new();
168 packet.encode(&mut buf).unwrap();
169
170 let fixed_header = FixedHeader::decode(&mut buf).unwrap();
171 assert_eq!(fixed_header.packet_type, PacketType::PubRel);
172 assert_eq!(fixed_header.flags, 0x02);
173
174 let decoded = PubRelPacket::decode_body(&mut buf, &fixed_header).unwrap();
175 assert_eq!(decoded.packet_id, 789);
176 assert_eq!(decoded.reason_code, ReasonCode::Success);
177 }
178
179 #[test]
180 fn test_pubrel_invalid_flags() {
181 let mut buf = BytesMut::new();
182 buf.put_u16(123);
183
184 let fixed_header = FixedHeader::new(PacketType::PubRel, 0x00, 2); let result = PubRelPacket::decode_body(&mut buf, &fixed_header);
186 assert!(result.is_err());
187 }
188}