mqtt5_protocol/packet/
pubcomp.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 PubCompPacket {
10 pub packet_id: u16,
12 pub reason_code: ReasonCode,
14 pub properties: Properties,
16}
17
18impl PubCompPacket {
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_pubcomp_reason_code(code: ReasonCode) -> bool {
55 matches!(
56 code,
57 ReasonCode::Success | ReasonCode::PacketIdentifierNotFound
58 )
59 }
60}
61
62impl MqttPacket for PubCompPacket {
63 fn packet_type(&self) -> PacketType {
64 PacketType::PubComp
65 }
66
67 fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
68 buf.put_u16(self.packet_id);
70
71 if self.reason_code != ReasonCode::Success || !self.properties.is_empty() {
74 buf.put_u8(u8::from(self.reason_code));
75 self.properties.encode(buf)?;
76 }
77
78 Ok(())
79 }
80
81 fn decode_body<B: Buf>(buf: &mut B, _fixed_header: &FixedHeader) -> Result<Self> {
82 if buf.remaining() < 2 {
84 return Err(MqttError::MalformedPacket(
85 "PUBCOMP missing packet identifier".to_string(),
86 ));
87 }
88 let packet_id = buf.get_u16();
89
90 let (reason_code, properties) = if buf.has_remaining() {
92 let reason_byte = buf.get_u8();
94 let code = ReasonCode::from_u8(reason_byte).ok_or_else(|| {
95 MqttError::MalformedPacket(format!("Invalid PUBCOMP reason code: {reason_byte}"))
96 })?;
97
98 if !Self::is_valid_pubcomp_reason_code(code) {
99 return Err(MqttError::MalformedPacket(format!(
100 "Invalid PUBCOMP reason code: {code:?}"
101 )));
102 }
103
104 let props = if buf.has_remaining() {
106 Properties::decode(buf)?
107 } else {
108 Properties::default()
109 };
110
111 (code, props)
112 } else {
113 (ReasonCode::Success, Properties::default())
115 };
116
117 Ok(Self {
118 packet_id,
119 reason_code,
120 properties,
121 })
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128 use crate::protocol::v5::properties::PropertyId;
129 use bytes::BytesMut;
130
131 #[test]
132 fn test_pubcomp_basic() {
133 let packet = PubCompPacket::new(123);
134
135 assert_eq!(packet.packet_id, 123);
136 assert_eq!(packet.reason_code, ReasonCode::Success);
137 assert!(packet.properties.is_empty());
138 }
139
140 #[test]
141 fn test_pubcomp_with_reason() {
142 let packet = PubCompPacket::new_with_reason(456, ReasonCode::PacketIdentifierNotFound)
143 .with_reason_string("Packet ID not found".to_string());
144
145 assert_eq!(packet.packet_id, 456);
146 assert_eq!(packet.reason_code, ReasonCode::PacketIdentifierNotFound);
147 assert!(packet.properties.contains(PropertyId::ReasonString));
148 }
149
150 #[test]
151 fn test_pubcomp_encode_decode() {
152 let packet = PubCompPacket::new(789);
153
154 let mut buf = BytesMut::new();
155 packet.encode(&mut buf).unwrap();
156
157 let fixed_header = FixedHeader::decode(&mut buf).unwrap();
158 assert_eq!(fixed_header.packet_type, PacketType::PubComp);
159
160 let decoded = PubCompPacket::decode_body(&mut buf, &fixed_header).unwrap();
161 assert_eq!(decoded.packet_id, 789);
162 assert_eq!(decoded.reason_code, ReasonCode::Success);
163 }
164
165 #[test]
166 fn test_pubcomp_encode_decode_with_properties() {
167 let packet = PubCompPacket::new(999)
168 .with_user_property("status".to_string(), "completed".to_string());
169
170 let mut buf = BytesMut::new();
171 packet.encode(&mut buf).unwrap();
172
173 let fixed_header = FixedHeader::decode(&mut buf).unwrap();
174 let decoded = PubCompPacket::decode_body(&mut buf, &fixed_header).unwrap();
175
176 assert_eq!(decoded.packet_id, 999);
177 assert!(decoded.properties.contains(PropertyId::UserProperty));
178 }
179}