mqtt5_protocol/packet/
pubrec.rs1use crate::error::{MqttError, Result};
2use crate::packet::{AckPacketHeader, FixedHeader, MqttPacket, PacketType};
3use crate::protocol::v5::properties::Properties;
4use crate::types::ReasonCode;
5use bytes::{Buf, BufMut};
6
7#[derive(Debug, Clone)]
9pub struct PubRecPacket {
10 pub packet_id: u16,
12 pub reason_code: ReasonCode,
14 pub properties: Properties,
16}
17
18impl PubRecPacket {
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_pubrec_reason_code(code: ReasonCode) -> bool {
55 matches!(
56 code,
57 ReasonCode::Success
58 | ReasonCode::NoMatchingSubscribers
59 | ReasonCode::UnspecifiedError
60 | ReasonCode::ImplementationSpecificError
61 | ReasonCode::NotAuthorized
62 | ReasonCode::TopicNameInvalid
63 | ReasonCode::PacketIdentifierInUse
64 | ReasonCode::QuotaExceeded
65 | ReasonCode::PayloadFormatInvalid
66 )
67 }
68
69 #[must_use]
71 pub fn create_header(&self) -> AckPacketHeader {
72 AckPacketHeader::create(self.packet_id, self.reason_code)
73 }
74
75 pub fn from_header(header: AckPacketHeader, properties: Properties) -> Result<Self> {
81 let reason_code = header.get_reason_code().ok_or_else(|| {
82 MqttError::MalformedPacket(format!(
83 "Invalid PUBREC reason code: 0x{:02X}",
84 header.reason_code
85 ))
86 })?;
87
88 if !Self::is_valid_pubrec_reason_code(reason_code) {
89 return Err(MqttError::MalformedPacket(format!(
90 "Invalid PUBREC reason code: {reason_code:?}"
91 )));
92 }
93
94 Ok(Self {
95 packet_id: header.packet_id,
96 reason_code,
97 properties,
98 })
99 }
100}
101
102impl MqttPacket for PubRecPacket {
103 fn packet_type(&self) -> PacketType {
104 PacketType::PubRec
105 }
106
107 fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
108 buf.put_u16(self.packet_id);
110
111 if self.reason_code != ReasonCode::Success || !self.properties.is_empty() {
114 buf.put_u8(u8::from(self.reason_code));
115 self.properties.encode(buf)?;
116 }
117
118 Ok(())
119 }
120
121 fn decode_body<B: Buf>(buf: &mut B, _fixed_header: &FixedHeader) -> Result<Self> {
122 if buf.remaining() < 2 {
124 return Err(MqttError::MalformedPacket(
125 "PUBREC missing packet identifier".to_string(),
126 ));
127 }
128 let packet_id = buf.get_u16();
129
130 let (reason_code, properties) = if buf.has_remaining() {
132 let reason_byte = buf.get_u8();
134 let code = ReasonCode::from_u8(reason_byte).ok_or_else(|| {
135 MqttError::MalformedPacket(format!("Invalid PUBREC reason code: {reason_byte}"))
136 })?;
137
138 if !Self::is_valid_pubrec_reason_code(code) {
139 return Err(MqttError::MalformedPacket(format!(
140 "Invalid PUBREC reason code: {code:?}"
141 )));
142 }
143
144 let props = if buf.has_remaining() {
146 Properties::decode(buf)?
147 } else {
148 Properties::default()
149 };
150
151 (code, props)
152 } else {
153 (ReasonCode::Success, Properties::default())
155 };
156
157 Ok(Self {
158 packet_id,
159 reason_code,
160 properties,
161 })
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use crate::protocol::v5::properties::PropertyId;
169 use bytes::BytesMut;
170
171 #[test]
172 fn test_pubrec_basic() {
173 let packet = PubRecPacket::new(123);
174
175 assert_eq!(packet.packet_id, 123);
176 assert_eq!(packet.reason_code, ReasonCode::Success);
177 assert!(packet.properties.is_empty());
178 }
179
180 #[test]
181 fn test_pubrec_with_reason() {
182 let packet = PubRecPacket::new_with_reason(456, ReasonCode::QuotaExceeded)
183 .with_reason_string("Quota exceeded for client".to_string());
184
185 assert_eq!(packet.packet_id, 456);
186 assert_eq!(packet.reason_code, ReasonCode::QuotaExceeded);
187 assert!(packet.properties.contains(PropertyId::ReasonString));
188 }
189
190 #[test]
191 fn test_pubrec_encode_decode() {
192 let packet = PubRecPacket::new(789);
193
194 let mut buf = BytesMut::new();
195 packet.encode(&mut buf).unwrap();
196
197 let fixed_header = FixedHeader::decode(&mut buf).unwrap();
198 assert_eq!(fixed_header.packet_type, PacketType::PubRec);
199
200 let decoded = PubRecPacket::decode_body(&mut buf, &fixed_header).unwrap();
201 assert_eq!(decoded.packet_id, 789);
202 assert_eq!(decoded.reason_code, ReasonCode::Success);
203 }
204}