rust_mqtt/packet/v5/
property.rs

1/*
2 * MIT License
3 *
4 * Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25use crate::encoding::variable_byte_integer::VariableByteIntegerEncoder;
26use crate::utils::buffer_reader::BuffReader;
27use crate::utils::buffer_writer::BuffWriter;
28use crate::utils::types::{BinaryData, BufferError, EncodedString, StringPair};
29
30#[derive(Debug, Clone)]
31pub enum Property<'a> {
32    PayloadFormat(u8),
33    MessageExpiryInterval(u32),
34    ContentType(EncodedString<'a>),
35    ResponseTopic(EncodedString<'a>),
36    CorrelationData(BinaryData<'a>),
37    SubscriptionIdentifier(u32),
38    SessionExpiryInterval(u32),
39    AssignedClientIdentifier(EncodedString<'a>),
40    ServerKeepAlive(u16),
41    AuthenticationMethod(EncodedString<'a>),
42    AuthenticationData(BinaryData<'a>),
43    RequestProblemInformation(u8),
44    WillDelayInterval(u32),
45    RequestResponseInformation(u8),
46    ResponseInformation(EncodedString<'a>),
47    ServerReference(EncodedString<'a>),
48    ReasonString(EncodedString<'a>),
49    ReceiveMaximum(u16),
50    TopicAliasMaximum(u16),
51    TopicAlias(u16),
52    MaximumQoS(u8),
53    RetainAvailable(u8),
54    UserProperty(StringPair<'a>),
55    MaximumPacketSize(u32),
56    WildcardSubscriptionAvailable(u8),
57    SubscriptionIdentifierAvailable(u8),
58    SharedSubscriptionAvailable(u8),
59    Reserved(),
60}
61
62impl<'a> Property<'a> {
63    pub fn connect_property(&self) -> bool {
64        // not possible to use with associated values with different types
65        #[allow(clippy::match_like_matches_macro)]
66        match self {
67            Property::SessionExpiryInterval(_u) => true,
68            Property::ReceiveMaximum(_u) => true,
69            Property::MaximumPacketSize(_u) => true,
70            Property::TopicAliasMaximum(_u) => true,
71            Property::RequestResponseInformation(_u) => true,
72            Property::RequestProblemInformation(_u) => true,
73            Property::UserProperty(_u) => true,
74            Property::AuthenticationMethod(_u) => true,
75            Property::AuthenticationData(_u) => true,
76            _ => false,
77        }
78    }
79
80    pub fn connack_property(&self) -> bool {
81        // not possible to use with associated values with different types
82        #[allow(clippy::match_like_matches_macro)]
83        match self {
84            Property::SessionExpiryInterval(_u) => true,
85            Property::ReceiveMaximum(_u) => true,
86            Property::MaximumQoS(_u) => true,
87            Property::MaximumPacketSize(_u) => true,
88            Property::AssignedClientIdentifier(_u) => true,
89            Property::TopicAliasMaximum(_u) => true,
90            Property::ReasonString(_u) => true,
91            Property::UserProperty(_u) => true,
92            Property::WildcardSubscriptionAvailable(_u) => true,
93            Property::SubscriptionIdentifierAvailable(_u) => true,
94            Property::SharedSubscriptionAvailable(_u) => true,
95            Property::ServerKeepAlive(_u) => true,
96            Property::ResponseInformation(_u) => true,
97            Property::ServerReference(_u) => true,
98            Property::AuthenticationMethod(_u) => true,
99            Property::AuthenticationData(_u) => true,
100            _ => false,
101        }
102    }
103
104    pub fn publish_property(&self) -> bool {
105        // not possible to use with associated values with different types
106        #[allow(clippy::match_like_matches_macro)]
107        match self {
108            Property::PayloadFormat(_u) => true,
109            Property::MessageExpiryInterval(_u) => true,
110            Property::TopicAlias(_u) => true,
111            Property::ResponseTopic(_u) => true,
112            Property::CorrelationData(_u) => true,
113            Property::UserProperty(_u) => true,
114            Property::SubscriptionIdentifier(_u) => true,
115            Property::ContentType(_u) => true,
116            _ => false,
117        }
118    }
119
120    pub fn puback_property(&self) -> bool {
121        // not possible to use with associated values with different types
122        #[allow(clippy::match_like_matches_macro)]
123        match self {
124            Property::ReasonString(_u) => true,
125            Property::UserProperty(_u) => true,
126            _ => false,
127        }
128    }
129
130    pub fn pubrec_property(&self) -> bool {
131        // not possible to use with associated values with different types
132        #[allow(clippy::match_like_matches_macro)]
133        match self {
134            Property::ReasonString(_u) => true,
135            Property::UserProperty(_u) => true,
136            _ => false,
137        }
138    }
139
140    pub fn pubrel_property(&self) -> bool {
141        // not possible to use with associated values with different types
142        #[allow(clippy::match_like_matches_macro)]
143        match self {
144            Property::ReasonString(_u) => true,
145            Property::UserProperty(_u) => true,
146            _ => false,
147        }
148    }
149
150    pub fn pubcomp_property(&self) -> bool {
151        // not possible to use with associated values with different types
152        #[allow(clippy::match_like_matches_macro)]
153        match self {
154            Property::ReasonString(_u) => true,
155            Property::UserProperty(_u) => true,
156            _ => false,
157        }
158    }
159
160    pub fn subscribe_property(&self) -> bool {
161        // not possible to use with associated values with different types
162        #[allow(clippy::match_like_matches_macro)]
163        match self {
164            Property::SubscriptionIdentifier(_u) => true,
165            Property::UserProperty(_u) => true,
166            _ => false,
167        }
168    }
169
170    pub fn suback_property(&self) -> bool {
171        // not possible to use with associated values with different types
172        #[allow(clippy::match_like_matches_macro)]
173        match self {
174            Property::ReasonString(_u) => true,
175            Property::UserProperty(_u) => true,
176            _ => false,
177        }
178    }
179
180    pub fn unsubscribe_property(&self) -> bool {
181        matches!(self, Property::UserProperty(_u))
182    }
183
184    pub fn unsuback_property(&self) -> bool {
185        // not possible to use with associated values with different types
186        #[allow(clippy::match_like_matches_macro)]
187        match self {
188            Property::ReasonString(_u) => true,
189            Property::UserProperty(_u) => true,
190            _ => false,
191        }
192    }
193
194    pub fn pingreq_property(&self) -> bool {
195        warn!("pingreq property list is incomplete");
196        false
197    }
198
199    pub fn pingresp_property(&self) -> bool {
200        warn!("pingresp property list is incomplete");
201        false
202    }
203
204    pub fn disconnect_property(&self) -> bool {
205        // not possible to use with associated values with different types
206        #[allow(clippy::match_like_matches_macro)]
207        match self {
208            Property::SessionExpiryInterval(_u) => true,
209            Property::ReasonString(_u) => true,
210            Property::UserProperty(_u) => true,
211            Property::ServerReference(_u) => true,
212            _ => false,
213        }
214    }
215
216    pub fn auth_property(&self) -> bool {
217        // not possible to use with associated values with different types
218        #[allow(clippy::match_like_matches_macro)]
219        match self {
220            Property::AuthenticationMethod(_u) => true,
221            Property::AuthenticationData(_u) => true,
222            Property::ReasonString(_u) => true,
223            Property::UserProperty(_u) => true,
224            _ => false,
225        }
226    }
227
228    pub fn encoded_len(&self) -> u16 {
229        match self {
230            Property::PayloadFormat(_u) => 1,
231            Property::MessageExpiryInterval(_u) => 4,
232            Property::ContentType(u) => u.encoded_len(),
233            Property::ResponseTopic(u) => u.encoded_len(),
234            Property::CorrelationData(u) => u.encoded_len(),
235            Property::SubscriptionIdentifier(u) => {
236                VariableByteIntegerEncoder::len(VariableByteIntegerEncoder::encode(*u).unwrap())
237                    as u16
238            }
239            Property::SessionExpiryInterval(_u) => 4,
240            Property::AssignedClientIdentifier(u) => u.encoded_len(),
241            Property::ServerKeepAlive(_u) => 2,
242            Property::AuthenticationMethod(u) => u.encoded_len(),
243            Property::AuthenticationData(u) => u.encoded_len(),
244            Property::RequestProblemInformation(_u) => 1,
245            Property::WillDelayInterval(_u) => 4,
246            Property::RequestResponseInformation(_u) => 1,
247            Property::ResponseInformation(u) => u.encoded_len(),
248            Property::ServerReference(u) => u.encoded_len(),
249            Property::ReasonString(u) => u.encoded_len(),
250            Property::ReceiveMaximum(_u) => 2,
251            Property::TopicAliasMaximum(_u) => 2,
252            Property::TopicAlias(_u) => 2,
253            Property::MaximumQoS(_u) => 1,
254            Property::RetainAvailable(_u) => 1,
255            Property::UserProperty(u) => u.encoded_len(),
256            Property::MaximumPacketSize(_u) => 4,
257            Property::WildcardSubscriptionAvailable(_u) => 1,
258            Property::SubscriptionIdentifierAvailable(_u) => 1,
259            Property::SharedSubscriptionAvailable(_u) => 1,
260            _ => 0,
261        }
262    }
263
264    pub fn encode(&self, buff_writer: &mut BuffWriter<'a>) -> Result<(), BufferError> {
265        match self {
266            Property::PayloadFormat(u) => buff_writer.write_u8(*u),
267            Property::MessageExpiryInterval(u) => buff_writer.write_u32(*u),
268            Property::ContentType(u) => buff_writer.write_string_ref(u),
269            Property::ResponseTopic(u) => buff_writer.write_string_ref(u),
270            Property::CorrelationData(u) => buff_writer.write_binary_ref(u),
271            Property::SubscriptionIdentifier(u) => buff_writer.write_variable_byte_int(*u),
272            Property::SessionExpiryInterval(u) => buff_writer.write_u32(*u),
273            Property::AssignedClientIdentifier(u) => buff_writer.write_string_ref(u),
274            Property::ServerKeepAlive(u) => buff_writer.write_u16(*u),
275            Property::AuthenticationMethod(u) => buff_writer.write_string_ref(u),
276            Property::AuthenticationData(u) => buff_writer.write_binary_ref(u),
277            Property::RequestProblemInformation(u) => buff_writer.write_u8(*u),
278            Property::WillDelayInterval(u) => buff_writer.write_u32(*u),
279            Property::RequestResponseInformation(u) => buff_writer.write_u8(*u),
280            Property::ResponseInformation(u) => buff_writer.write_string_ref(u),
281            Property::ServerReference(u) => buff_writer.write_string_ref(u),
282            Property::ReasonString(u) => buff_writer.write_string_ref(u),
283            Property::ReceiveMaximum(u) => buff_writer.write_u16(*u),
284            Property::TopicAliasMaximum(u) => buff_writer.write_u16(*u),
285            Property::TopicAlias(u) => buff_writer.write_u16(*u),
286            Property::MaximumQoS(u) => buff_writer.write_u8(*u),
287            Property::RetainAvailable(u) => buff_writer.write_u8(*u),
288            Property::UserProperty(u) => buff_writer.write_string_pair_ref(u),
289            Property::MaximumPacketSize(u) => buff_writer.write_u32(*u),
290            Property::WildcardSubscriptionAvailable(u) => buff_writer.write_u8(*u),
291            Property::SubscriptionIdentifierAvailable(u) => buff_writer.write_u8(*u),
292            Property::SharedSubscriptionAvailable(u) => buff_writer.write_u8(*u),
293            _ => Err(BufferError::PropertyNotFound),
294        }
295    }
296
297    pub fn decode(buff_reader: &mut BuffReader<'a>) -> Result<Property<'a>, BufferError> {
298        let property_identifier = buff_reader.read_u8();
299        return match property_identifier {
300            Ok(0x01) => Ok(Property::PayloadFormat(buff_reader.read_u8()?)),
301            Ok(0x02) => Ok(Property::MessageExpiryInterval(buff_reader.read_u32()?)),
302            Ok(0x03) => Ok(Property::ContentType(buff_reader.read_string()?)),
303            Ok(0x08) => Ok(Property::ResponseTopic(buff_reader.read_string()?)),
304            Ok(0x09) => Ok(Property::CorrelationData(buff_reader.read_binary()?)),
305            Ok(0x0B) => Ok(Property::SubscriptionIdentifier(
306                buff_reader.read_variable_byte_int()?,
307            )),
308            Ok(0x11) => Ok(Property::SessionExpiryInterval(buff_reader.read_u32()?)),
309            Ok(0x12) => Ok(Property::AssignedClientIdentifier(
310                buff_reader.read_string()?,
311            )),
312            Ok(0x13) => Ok(Property::ServerKeepAlive(buff_reader.read_u16()?)),
313            Ok(0x15) => Ok(Property::AuthenticationMethod(buff_reader.read_string()?)),
314            Ok(0x16) => Ok(Property::AuthenticationData(buff_reader.read_binary()?)),
315            Ok(0x17) => Ok(Property::RequestProblemInformation(buff_reader.read_u8()?)),
316            Ok(0x18) => Ok(Property::WillDelayInterval(buff_reader.read_u32()?)),
317            Ok(0x19) => Ok(Property::RequestResponseInformation(buff_reader.read_u8()?)),
318            Ok(0x1A) => Ok(Property::ResponseInformation(buff_reader.read_string()?)),
319            Ok(0x1C) => Ok(Property::ServerReference(buff_reader.read_string()?)),
320            Ok(0x1F) => Ok(Property::ReasonString(buff_reader.read_string()?)),
321            Ok(0x21) => Ok(Property::ReceiveMaximum(buff_reader.read_u16()?)),
322            Ok(0x22) => Ok(Property::TopicAliasMaximum(buff_reader.read_u16()?)),
323            Ok(0x23) => Ok(Property::TopicAlias(buff_reader.read_u16()?)),
324            Ok(0x24) => Ok(Property::MaximumQoS(buff_reader.read_u8()?)),
325            Ok(0x25) => Ok(Property::RetainAvailable(buff_reader.read_u8()?)),
326            Ok(0x26) => Ok(Property::UserProperty(buff_reader.read_string_pair()?)),
327            Ok(0x27) => Ok(Property::MaximumPacketSize(buff_reader.read_u32()?)),
328            Ok(0x28) => Ok(Property::WildcardSubscriptionAvailable(
329                buff_reader.read_u8()?,
330            )),
331            Ok(0x29) => Ok(Property::SubscriptionIdentifierAvailable(
332                buff_reader.read_u8()?,
333            )),
334            Ok(0x2A) => Ok(Property::SharedSubscriptionAvailable(
335                buff_reader.read_u8()?,
336            )),
337            Err(err) => Err(err),
338            _ => Err(BufferError::IdNotFound),
339        };
340    }
341}
342
343impl<'a> From<&Property<'a>> for u8 {
344    fn from(value: &Property<'a>) -> Self {
345        match value {
346            Property::PayloadFormat(_u) => 0x01,
347            Property::MessageExpiryInterval(_u) => 0x02,
348            Property::ContentType(_u) => 0x03,
349            Property::ResponseTopic(_u) => 0x08,
350            Property::CorrelationData(_u) => 0x09,
351            Property::SubscriptionIdentifier(_u) => 0x0B,
352            Property::SessionExpiryInterval(_u) => 0x11,
353            Property::AssignedClientIdentifier(_u) => 0x12,
354            Property::ServerKeepAlive(_u) => 0x13,
355            Property::AuthenticationMethod(_u) => 0x15,
356            Property::AuthenticationData(_u) => 0x16,
357            Property::RequestProblemInformation(_u) => 0x17,
358            Property::WillDelayInterval(_u) => 0x18,
359            Property::RequestResponseInformation(_u) => 0x19,
360            Property::ResponseInformation(_u) => 0x1A,
361            Property::ServerReference(_u) => 0x1C,
362            Property::ReasonString(_u) => 0x1F,
363            Property::ReceiveMaximum(_u) => 0x21,
364            Property::TopicAliasMaximum(_u) => 0x22,
365            Property::TopicAlias(_u) => 0x23,
366            Property::MaximumQoS(_u) => 0x24,
367            Property::RetainAvailable(_u) => 0x25,
368            Property::UserProperty(_u) => 0x26,
369            Property::MaximumPacketSize(_u) => 0x27,
370            Property::WildcardSubscriptionAvailable(_u) => 0x28,
371            Property::SubscriptionIdentifierAvailable(_u) => 0x29,
372            Property::SharedSubscriptionAvailable(_u) => 0x2A,
373            _ => 0x00,
374        }
375    }
376}
377
378impl<'a> From<u8> for Property<'a> {
379    fn from(_orig: u8) -> Self {
380        warn!("Deserialization of Properties from u8 is not implemented");
381        Property::Reserved()
382    }
383}