mqtt5_protocol/packet/
ack_common.rs

1use crate::types::ReasonCode;
2
3macro_rules! define_ack_packet {
4    (
5        $(#[$meta:meta])*
6        $vis:vis struct $name:ident;
7        packet_type = $packet_type:expr;
8        validator = $validator:path;
9        error_prefix = $error_prefix:literal;
10    ) => {
11        $crate::packet::ack_common::define_ack_packet_inner! {
12            $(#[$meta])*
13            $vis struct $name;
14            packet_type = $packet_type;
15            validator = $validator;
16            error_prefix = $error_prefix;
17            flags = (None);
18        }
19    };
20    (
21        $(#[$meta:meta])*
22        $vis:vis struct $name:ident;
23        packet_type = $packet_type:expr;
24        validator = $validator:path;
25        error_prefix = $error_prefix:literal;
26        flags = $flags:literal;
27        validate_flags = true;
28    ) => {
29        $crate::packet::ack_common::define_ack_packet_inner! {
30            $(#[$meta])*
31            $vis struct $name;
32            packet_type = $packet_type;
33            validator = $validator;
34            error_prefix = $error_prefix;
35            flags = (Some($flags));
36        }
37    };
38}
39
40#[doc(hidden)]
41macro_rules! ack_impl_flags {
42    ((None)) => {};
43    ((Some($val:literal))) => {
44        fn flags(&self) -> u8 {
45            $val
46        }
47    };
48}
49
50#[doc(hidden)]
51macro_rules! ack_validate_flags {
52    ((None), $fh:ident, $prefix:literal) => {
53        let _ = $fh;
54    };
55    ((Some($val:literal)), $fh:ident, $prefix:literal) => {
56        if $fh.flags != $val {
57            return Err($crate::error::MqttError::MalformedPacket(format!(
58                concat!(
59                    "Invalid ",
60                    $prefix,
61                    " flags: expected 0x{:02X}, got 0x{:02X}"
62                ),
63                $val, $fh.flags
64            )));
65        }
66    };
67}
68
69#[doc(hidden)]
70macro_rules! define_ack_packet_inner {
71    (
72        $(#[$meta:meta])*
73        $vis:vis struct $name:ident;
74        packet_type = $packet_type:expr;
75        validator = $validator:path;
76        error_prefix = $error_prefix:literal;
77        flags = $flags:tt;
78    ) => {
79        $(#[$meta])*
80        #[derive(Debug, Clone)]
81        $vis struct $name {
82            pub packet_id: u16,
83            pub reason_code: $crate::types::ReasonCode,
84            pub properties: $crate::protocol::v5::properties::Properties,
85        }
86
87        impl $name {
88            #[must_use]
89            pub fn new(packet_id: u16) -> Self {
90                Self {
91                    packet_id,
92                    reason_code: $crate::types::ReasonCode::Success,
93                    properties: $crate::protocol::v5::properties::Properties::default(),
94                }
95            }
96
97            #[must_use]
98            pub fn new_with_reason(packet_id: u16, reason_code: $crate::types::ReasonCode) -> Self {
99                Self {
100                    packet_id,
101                    reason_code,
102                    properties: $crate::protocol::v5::properties::Properties::default(),
103                }
104            }
105
106            #[must_use]
107            pub fn with_reason_string(mut self, reason: String) -> Self {
108                self.properties.set_reason_string(reason);
109                self
110            }
111
112            #[must_use]
113            pub fn with_user_property(mut self, key: String, value: String) -> Self {
114                self.properties.add_user_property(key, value);
115                self
116            }
117        }
118
119        impl $crate::packet::MqttPacket for $name {
120            fn packet_type(&self) -> $crate::packet::PacketType {
121                $packet_type
122            }
123
124            $crate::packet::ack_common::ack_impl_flags!($flags);
125
126            fn encode_body<B: bytes::BufMut>(&self, buf: &mut B) -> $crate::error::Result<()> {
127                buf.put_u16(self.packet_id);
128                if self.reason_code != $crate::types::ReasonCode::Success || !self.properties.is_empty() {
129                    buf.put_u8(u8::from(self.reason_code));
130                    self.properties.encode(buf)?;
131                }
132                Ok(())
133            }
134
135            fn decode_body<B: bytes::Buf>(
136                buf: &mut B,
137                fixed_header: &$crate::packet::FixedHeader,
138            ) -> $crate::error::Result<Self> {
139                $crate::packet::ack_common::ack_validate_flags!($flags, fixed_header, $error_prefix);
140
141                if buf.remaining() < 2 {
142                    return Err($crate::error::MqttError::MalformedPacket(
143                        concat!($error_prefix, " missing packet identifier").to_string(),
144                    ));
145                }
146                let packet_id = buf.get_u16();
147
148                let (reason_code, properties) = if buf.has_remaining() {
149                    let reason_byte = buf.get_u8();
150                    let code = $crate::types::ReasonCode::from_u8(reason_byte).ok_or_else(|| {
151                        $crate::error::MqttError::MalformedPacket(format!(
152                            concat!("Invalid ", $error_prefix, " reason code: {}"),
153                            reason_byte
154                        ))
155                    })?;
156
157                    if !$validator(code) {
158                        return Err($crate::error::MqttError::MalformedPacket(format!(
159                            concat!("Invalid ", $error_prefix, " reason code: {:?}"),
160                            code
161                        )));
162                    }
163
164                    let props = if buf.has_remaining() {
165                        $crate::protocol::v5::properties::Properties::decode(buf)?
166                    } else {
167                        $crate::protocol::v5::properties::Properties::default()
168                    };
169
170                    (code, props)
171                } else {
172                    ($crate::types::ReasonCode::Success, $crate::protocol::v5::properties::Properties::default())
173                };
174
175                Ok(Self {
176                    packet_id,
177                    reason_code,
178                    properties,
179                })
180            }
181        }
182    };
183}
184
185pub(crate) use ack_impl_flags;
186pub(crate) use ack_validate_flags;
187pub(crate) use define_ack_packet;
188pub(crate) use define_ack_packet_inner;
189
190pub fn is_valid_publish_ack_reason_code(code: ReasonCode) -> bool {
191    matches!(
192        code,
193        ReasonCode::Success
194            | ReasonCode::NoMatchingSubscribers
195            | ReasonCode::UnspecifiedError
196            | ReasonCode::ImplementationSpecificError
197            | ReasonCode::NotAuthorized
198            | ReasonCode::TopicNameInvalid
199            | ReasonCode::PacketIdentifierInUse
200            | ReasonCode::QuotaExceeded
201            | ReasonCode::PayloadFormatInvalid
202    )
203}
204
205pub fn is_valid_pubrel_reason_code(code: ReasonCode) -> bool {
206    matches!(
207        code,
208        ReasonCode::Success | ReasonCode::PacketIdentifierNotFound
209    )
210}