mqtt5_protocol/packet/
pubrec.rs

1use super::ack_common::{define_ack_packet, is_valid_publish_ack_reason_code};
2use crate::error::{MqttError, Result};
3use crate::packet::{AckPacketHeader, PacketType};
4use crate::protocol::v5::properties::Properties;
5
6define_ack_packet! {
7    /// MQTT PUBREC packet (`QoS` 2 publish received, part 1)
8    pub struct PubRecPacket;
9    packet_type = PacketType::PubRec;
10    validator = is_valid_publish_ack_reason_code;
11    error_prefix = "PUBREC";
12}
13
14impl PubRecPacket {
15    #[must_use]
16    pub fn create_header(&self) -> AckPacketHeader {
17        AckPacketHeader::create(self.packet_id, self.reason_code)
18    }
19
20    /// # Errors
21    /// Returns an error if the reason code in the header is invalid
22    pub fn from_header(header: AckPacketHeader, properties: Properties) -> Result<Self> {
23        let reason_code = header.get_reason_code().ok_or_else(|| {
24            MqttError::MalformedPacket(format!(
25                "Invalid PUBREC reason code: 0x{:02X}",
26                header.reason_code
27            ))
28        })?;
29
30        if !is_valid_publish_ack_reason_code(reason_code) {
31            return Err(MqttError::MalformedPacket(format!(
32                "Invalid PUBREC reason code: {reason_code:?}"
33            )));
34        }
35
36        Ok(Self {
37            packet_id: header.packet_id,
38            reason_code,
39            properties,
40        })
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47    use crate::packet::{FixedHeader, MqttPacket};
48    use crate::protocol::v5::properties::PropertyId;
49    use crate::types::ReasonCode;
50    use bytes::{BufMut, BytesMut};
51
52    #[test]
53    fn test_pubrec_basic() {
54        let packet = PubRecPacket::new(123);
55
56        assert_eq!(packet.packet_id, 123);
57        assert_eq!(packet.reason_code, ReasonCode::Success);
58        assert!(packet.properties.is_empty());
59    }
60
61    #[test]
62    fn test_pubrec_with_reason() {
63        let packet = PubRecPacket::new_with_reason(456, ReasonCode::QuotaExceeded)
64            .with_reason_string("Quota exceeded for client".to_string());
65
66        assert_eq!(packet.packet_id, 456);
67        assert_eq!(packet.reason_code, ReasonCode::QuotaExceeded);
68        assert!(packet.properties.contains(PropertyId::ReasonString));
69    }
70
71    #[test]
72    fn test_pubrec_encode_decode() {
73        let packet = PubRecPacket::new(789);
74
75        let mut buf = BytesMut::new();
76        packet.encode(&mut buf).unwrap();
77
78        let fixed_header = FixedHeader::decode(&mut buf).unwrap();
79        assert_eq!(fixed_header.packet_type, PacketType::PubRec);
80
81        let decoded = PubRecPacket::decode_body(&mut buf, &fixed_header).unwrap();
82        assert_eq!(decoded.packet_id, 789);
83        assert_eq!(decoded.reason_code, ReasonCode::Success);
84    }
85
86    #[test]
87    fn test_pubrec_v311_style() {
88        let mut buf = BytesMut::new();
89        buf.put_u16(1234);
90
91        let fixed_header = FixedHeader::new(PacketType::PubRec, 0, 2);
92        let decoded = PubRecPacket::decode_body(&mut buf, &fixed_header).unwrap();
93
94        assert_eq!(decoded.packet_id, 1234);
95        assert_eq!(decoded.reason_code, ReasonCode::Success);
96        assert!(decoded.properties.is_empty());
97    }
98
99    #[test]
100    fn test_pubrec_invalid_reason_code() {
101        let mut buf = BytesMut::new();
102        buf.put_u16(123);
103        buf.put_u8(0xFF);
104
105        let fixed_header = FixedHeader::new(PacketType::PubRec, 0, 3);
106        let result = PubRecPacket::decode_body(&mut buf, &fixed_header);
107        assert!(result.is_err());
108    }
109
110    #[test]
111    fn test_pubrec_missing_packet_id() {
112        let mut buf = BytesMut::new();
113        buf.put_u8(0);
114
115        let fixed_header = FixedHeader::new(PacketType::PubRec, 0, 1);
116        let result = PubRecPacket::decode_body(&mut buf, &fixed_header);
117        assert!(result.is_err());
118    }
119}