mqtt_proto/v5/
types.rs

1use std::convert::TryFrom;
2use std::fmt;
3use std::sync::Arc;
4
5use bytes::Bytes;
6use tokio::io::AsyncRead;
7
8use super::ErrorV5;
9use crate::{read_bytes, read_string, read_u16, read_u32, read_u8, Error, TopicName};
10
11/// [Property identifier](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901027)
12///
13/// | Dec |  Hex | Name (usage)                      | Type                  | Packet / Will Properties                        |
14/// |-----|------|-----------------------------------|-----------------------|-------------------------------------------------|
15/// |   1 | 0x01 | Payload Format Indicator          | Byte                  | PUBLISH, Will Properties                        |
16/// |   2 | 0x02 | Message Expiry Interval           | Four Byte Integer     | PUBLISH, Will Properties                        |
17/// |   3 | 0x03 | Content Type                      | UTF-8 Encoded String  | PUBLISH, Will Properties                        |
18/// |   8 | 0x08 | Response Topic                    | UTF-8 Encoded String  | PUBLISH, Will Properties                        |
19/// |   9 | 0x09 | Correlation Data                  | Binary Data           | PUBLISH, Will Properties                        |
20/// |  11 | 0x0B | Subscription Identifier           | Variable Byte Integer | PUBLISH, SUBSCRIBE                              |
21/// |  17 | 0x11 | Session Expiry Interval           | Four Byte Integer     | CONNECT, CONNACK, DISCONNECT                    |
22/// |  18 | 0x12 | Assigned Client Identifier        | UTF-8 Encoded String  | CONNACK                                         |
23/// |  19 | 0x13 | Server Keep Alive                 | Two Byte Integer      | CONNACK                                         |
24/// |  21 | 0x15 | Authentication Method             | UTF-8 Encoded String  | CONNECT, CONNACK, AUTH                          |
25/// |  22 | 0x16 | Authentication Data               | Binary Data           | CONNECT, CONNACK, AUTH                          |
26/// |  23 | 0x17 | Request Problem Information       | Byte                  | CONNECT                                         |
27/// |  24 | 0x18 | Will Delay Interval               | Four Byte Integer     | Will Properties                                 |
28/// |  25 | 0x19 | Request Response Information      | Byte                  | CONNECT                                         |
29/// |  26 | 0x1A | Response Information              | UTF-8 Encoded String  | CONNACK                                         |
30/// |  28 | 0x1C | Server Reference                  | UTF-8 Encoded String  | CONNACK, DISCONNECT                             |
31/// |  31 | 0x1F | Reason String                     | UTF-8 Encoded String  | CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP,       |
32/// |     |      |                                   |                       | SUBACK, UNSUBACK, DISCONNECT, AUTH              |
33/// |  33 | 0x21 | Receive Maximum                   | Two Byte Integer      | CONNECT, CONNACK                                |
34/// |  34 | 0x22 | Topic Alias Maximum               | Two Byte Integer      | CONNECT, CONNACK                                |
35/// |  35 | 0x23 | Topic Alias                       | Two Byte Integer      | PUBLISH                                         |
36/// |  36 | 0x24 | Maximum QoS                       | Byte                  | CONNACK                                         |
37/// |  37 | 0x25 | Retain Available                  | Byte                  | CONNACK                                         |
38/// |  38 | 0x26 | User Property                     | UTF-8 String Pair     | CONNECT, CONNACK, PUBLISH, Will Properties,     |
39/// |     |      |                                   |                       | PUBACK, PUBREC, PUBREL, PUBCOMP, SUBSCRIBE,     |
40/// |     |      |                                   |                       | SUBACK, UNSUBSCRIBE, UNSUBACK, DISCONNECT, AUTH |
41/// |  39 | 0x27 | Maximum Packet Size               | Four Byte Integer     | CONNECT, CONNACK                                |
42/// |  40 | 0x28 | Wildcard Subscription Available   | Byte                  | CONNACK                                         |
43/// |  41 | 0x29 | Subscription Identifier Available | Byte                  | CONNACK                                         |
44/// |  42 | 0x2A | Shared Subscription Available     | Byte                  | CONNACK                                         |
45#[repr(u8)]
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum PropertyId {
48    PayloadFormatIndicator = 0x01,
49    MessageExpiryInterval = 0x02,
50    ContentType = 0x03,
51    ResponseTopic = 0x08,
52    CorrelationData = 0x09,
53    SubscriptionIdentifier = 0x0B,
54    SessionExpiryInterval = 0x11,
55    AssignedClientIdentifier = 0x12,
56    ServerKeepAlive = 0x13,
57    AuthenticationMethod = 0x15,
58    AuthenticationData = 0x16,
59    RequestProblemInformation = 0x17,
60    WillDelayInterval = 0x18,
61    RequestResponseInformation = 0x19,
62    ResponseInformation = 0x1A,
63    ServerReference = 0x1C,
64    ReasonString = 0x1F,
65    ReceiveMaximum = 0x21,
66    TopicAliasMaximum = 0x22,
67    TopicAlias = 0x23,
68    MaximumQoS = 0x24,
69    RetainAvailable = 0x25,
70    UserProperty = 0x26,
71    MaximumPacketSize = 0x27,
72    WildcardSubscriptionAvailable = 0x28,
73    SubscriptionIdentifierAvailable = 0x29,
74    SharedSubscriptionAvailable = 0x2A,
75}
76
77impl PropertyId {
78    pub fn from_u8(value: u8) -> Result<Self, ErrorV5> {
79        let typ = match value {
80            0x01 => Self::PayloadFormatIndicator,
81            0x02 => Self::MessageExpiryInterval,
82            0x03 => Self::ContentType,
83            0x08 => Self::ResponseTopic,
84            0x09 => Self::CorrelationData,
85            0x0B => Self::SubscriptionIdentifier,
86            0x11 => Self::SessionExpiryInterval,
87            0x12 => Self::AssignedClientIdentifier,
88            0x13 => Self::ServerKeepAlive,
89            0x15 => Self::AuthenticationMethod,
90            0x16 => Self::AuthenticationData,
91            0x17 => Self::RequestProblemInformation,
92            0x18 => Self::WillDelayInterval,
93            0x19 => Self::RequestResponseInformation,
94            0x1A => Self::ResponseInformation,
95            0x1C => Self::ServerReference,
96            0x1F => Self::ReasonString,
97            0x21 => Self::ReceiveMaximum,
98            0x22 => Self::TopicAliasMaximum,
99            0x23 => Self::TopicAlias,
100            0x24 => Self::MaximumQoS,
101            0x25 => Self::RetainAvailable,
102            0x26 => Self::UserProperty,
103            0x27 => Self::MaximumPacketSize,
104            0x28 => Self::WildcardSubscriptionAvailable,
105            0x29 => Self::SubscriptionIdentifierAvailable,
106            0x2A => Self::SharedSubscriptionAvailable,
107            _ => return Err(ErrorV5::InvalidPropertyId(value)),
108        };
109        Ok(typ)
110    }
111}
112
113impl fmt::Display for PropertyId {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        write!(f, "{self:?}")
116    }
117}
118
119// A helper type to decode/encode property value
120pub(crate) struct PropertyValue;
121
122impl PropertyValue {
123    #[inline]
124    pub(crate) async fn decode_bool<T: AsyncRead + Unpin>(
125        reader: &mut T,
126        property_id: PropertyId,
127        target: &mut Option<bool>,
128    ) -> Result<(), ErrorV5> {
129        if target.is_some() {
130            return Err(ErrorV5::DuplicatedProperty(property_id));
131        }
132        let value = read_u8(reader).await?;
133        if value > 1 {
134            Err(ErrorV5::InvalidByteProperty(property_id, value))
135        } else {
136            *target = Some(value == 1);
137            Ok(())
138        }
139    }
140
141    #[inline]
142    pub(crate) async fn decode_u16<T: AsyncRead + Unpin>(
143        reader: &mut T,
144        property_id: PropertyId,
145        target: &mut Option<u16>,
146    ) -> Result<(), ErrorV5> {
147        if target.is_some() {
148            return Err(ErrorV5::DuplicatedProperty(property_id));
149        }
150        *target = Some(read_u16(reader).await?);
151        Ok(())
152    }
153
154    #[inline]
155    pub(crate) async fn decode_u32<T: AsyncRead + Unpin>(
156        reader: &mut T,
157        property_id: PropertyId,
158        target: &mut Option<u32>,
159    ) -> Result<(), ErrorV5> {
160        if target.is_some() {
161            return Err(ErrorV5::DuplicatedProperty(property_id));
162        }
163        *target = Some(read_u32(reader).await?);
164        Ok(())
165    }
166
167    #[inline]
168    pub(crate) async fn decode_string<T: AsyncRead + Unpin>(
169        reader: &mut T,
170        property_id: PropertyId,
171        target: &mut Option<Arc<String>>,
172    ) -> Result<(), ErrorV5> {
173        if target.is_some() {
174            return Err(ErrorV5::DuplicatedProperty(property_id));
175        }
176        *target = Some(Arc::new(read_string(reader).await?));
177        Ok(())
178    }
179
180    #[inline]
181    pub(crate) async fn decode_topic_name<T: AsyncRead + Unpin>(
182        reader: &mut T,
183        property_id: PropertyId,
184        target: &mut Option<TopicName>,
185    ) -> Result<(), ErrorV5> {
186        if target.is_some() {
187            return Err(ErrorV5::DuplicatedProperty(property_id));
188        }
189        let content = read_string(reader).await?;
190        *target = Some(TopicName::try_from(content)?);
191        Ok(())
192    }
193
194    #[inline]
195    pub(crate) async fn decode_bytes<T: AsyncRead + Unpin>(
196        reader: &mut T,
197        property_id: PropertyId,
198        target: &mut Option<Bytes>,
199    ) -> Result<(), ErrorV5> {
200        if target.is_some() {
201            return Err(ErrorV5::DuplicatedProperty(property_id));
202        }
203        *target = Some(Bytes::from(read_bytes(reader).await?));
204        Ok(())
205    }
206
207    #[inline]
208    pub(crate) async fn decode_user_property<T: AsyncRead + Unpin>(
209        reader: &mut T,
210    ) -> Result<UserProperty, ErrorV5> {
211        let name = read_string(reader).await?;
212        let value = read_string(reader).await?;
213        Ok(UserProperty {
214            name: Arc::new(name),
215            value: Arc::new(value),
216        })
217    }
218}
219
220/// User Property is a UTF-8 String Pair.
221#[derive(Debug, Clone, PartialEq, Eq, Hash)]
222#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
223pub struct UserProperty {
224    /// The name of the user property.
225    pub name: Arc<String>,
226    /// The value of the user property.
227    pub value: Arc<String>,
228}
229
230/// Variable Byte Integer
231#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
232pub struct VarByteInt(u32);
233
234#[cfg(feature = "arbitrary")]
235impl<'a> arbitrary::Arbitrary<'a> for VarByteInt {
236    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
237        let value: u32 = u.arbitrary()?;
238        Ok(VarByteInt(value % 268435456))
239    }
240}
241
242impl VarByteInt {
243    pub fn value(self) -> u32 {
244        self.0
245    }
246}
247
248impl TryFrom<u32> for VarByteInt {
249    type Error = ErrorV5;
250    fn try_from(value: u32) -> Result<Self, ErrorV5> {
251        if value < 268435456 {
252            Ok(VarByteInt(value))
253        } else {
254            Err(Error::InvalidVarByteInt.into())
255        }
256    }
257}
258
259macro_rules! decode_property {
260    (PayloadFormatIndicator, $properties:expr, $reader:expr, $property_id:expr) => {
261        crate::v5::PropertyValue::decode_bool(
262            $reader,
263            $property_id,
264            &mut $properties.payload_is_utf8,
265        )
266        .await?;
267    };
268    (MessageExpiryInterval, $properties:expr, $reader:expr, $property_id:expr) => {
269        crate::v5::PropertyValue::decode_u32(
270            $reader,
271            $property_id,
272            &mut $properties.message_expiry_interval,
273        )
274        .await?;
275    };
276    (ContentType, $properties:expr, $reader:expr, $property_id:expr) => {
277        crate::v5::PropertyValue::decode_string(
278            $reader,
279            $property_id,
280            &mut $properties.content_type,
281        )
282        .await?;
283    };
284    (ResponseTopic, $properties:expr, $reader:expr, $property_id:expr) => {
285        crate::v5::PropertyValue::decode_topic_name(
286            $reader,
287            $property_id,
288            &mut $properties.response_topic,
289        )
290        .await
291        .map_err(|err| match err {
292            crate::v5::ErrorV5::Common(crate::Error::InvalidTopicName(_)) => {
293                crate::v5::ErrorV5::InvalidResponseTopic
294            }
295            err => err,
296        })?;
297    };
298    (CorrelationData, $properties:expr, $reader:expr, $property_id:expr) => {
299        crate::v5::PropertyValue::decode_bytes(
300            $reader,
301            $property_id,
302            &mut $properties.correlation_data,
303        )
304        .await?;
305    };
306    (SubscriptionIdentifier, $properties:expr, $reader:expr, $property_id:expr) => {
307        if $properties.subscription_id.is_some() {
308            return Err(crate::v5::ErrorV5::DuplicatedProperty($property_id));
309        }
310        let (value, _bytes) = crate::decode_var_int($reader).await?;
311        $properties.subscription_id = Some(crate::v5::VarByteInt::try_from(value)?);
312    };
313    (SessionExpiryInterval, $properties:expr, $reader:expr, $property_id:expr) => {
314        crate::v5::PropertyValue::decode_u32(
315            $reader,
316            $property_id,
317            &mut $properties.session_expiry_interval,
318        )
319        .await?;
320    };
321    (AssignedClientIdentifier, $properties:expr, $reader:expr, $property_id:expr) => {
322        crate::v5::PropertyValue::decode_string(
323            $reader,
324            $property_id,
325            &mut $properties.assigned_client_id,
326        )
327        .await?;
328    };
329    (ServerKeepAlive, $properties:expr, $reader:expr, $property_id:expr) => {
330        crate::v5::PropertyValue::decode_u16(
331            $reader,
332            $property_id,
333            &mut $properties.server_keep_alive,
334        )
335        .await?;
336    };
337    (AuthenticationMethod, $properties:expr, $reader:expr, $property_id:expr) => {
338        crate::v5::PropertyValue::decode_string(
339            $reader,
340            $property_id,
341            &mut $properties.auth_method,
342        )
343        .await?;
344    };
345    (AuthenticationData, $properties:expr, $reader:expr, $property_id:expr) => {
346        crate::v5::PropertyValue::decode_bytes($reader, $property_id, &mut $properties.auth_data)
347            .await?;
348    };
349    (RequestProblemInformation, $properties:expr, $reader:expr, $property_id:expr) => {
350        crate::v5::PropertyValue::decode_bool(
351            $reader,
352            $property_id,
353            &mut $properties.request_problem_info,
354        )
355        .await?;
356    };
357    (WillDelayInterval, $properties:expr, $reader:expr, $property_id:expr) => {
358        crate::v5::PropertyValue::decode_u32(
359            $reader,
360            $property_id,
361            &mut $properties.delay_interval,
362        )
363        .await?;
364    };
365    (RequestResponseInformation, $properties:expr, $reader:expr, $property_id:expr) => {
366        crate::v5::PropertyValue::decode_bool(
367            $reader,
368            $property_id,
369            &mut $properties.request_response_info,
370        )
371        .await?;
372    };
373    (ResponseInformation, $properties:expr, $reader:expr, $property_id:expr) => {
374        crate::v5::PropertyValue::decode_string(
375            $reader,
376            $property_id,
377            &mut $properties.response_info,
378        )
379        .await?;
380    };
381    (ServerReference, $properties:expr, $reader:expr, $property_id:expr) => {
382        crate::v5::PropertyValue::decode_string(
383            $reader,
384            $property_id,
385            &mut $properties.server_reference,
386        )
387        .await?;
388    };
389    (ReasonString, $properties:expr, $reader:expr, $property_id:expr) => {
390        crate::v5::PropertyValue::decode_string(
391            $reader,
392            $property_id,
393            &mut $properties.reason_string,
394        )
395        .await?;
396    };
397    (ReceiveMaximum, $properties:expr, $reader:expr, $property_id:expr) => {
398        crate::v5::PropertyValue::decode_u16($reader, $property_id, &mut $properties.receive_max)
399            .await?;
400    };
401    (TopicAliasMaximum, $properties:expr, $reader:expr, $property_id:expr) => {
402        crate::v5::PropertyValue::decode_u16(
403            $reader,
404            $property_id,
405            &mut $properties.topic_alias_max,
406        )
407        .await?;
408    };
409    (TopicAlias, $properties:expr, $reader:expr, $property_id:expr) => {
410        crate::v5::PropertyValue::decode_u16($reader, $property_id, &mut $properties.topic_alias)
411            .await?;
412    };
413    (MaximumQoS, $properties:expr, $reader:expr, $property_id:expr) => {
414        if $properties.max_qos.is_some() {
415            return Err(crate::v5::ErrorV5::DuplicatedProperty($property_id));
416        }
417        let value = crate::read_u8($reader).await?;
418        if value > 1 {
419            return Err(crate::v5::ErrorV5::InvalidByteProperty($property_id, value));
420        } else {
421            $properties.max_qos = Some(crate::QoS::from_u8(value).expect("0/1 qos"));
422        }
423    };
424    (RetainAvailable, $properties:expr, $reader:expr, $property_id:expr) => {
425        crate::v5::PropertyValue::decode_bool(
426            $reader,
427            $property_id,
428            &mut $properties.retain_available,
429        )
430        .await?;
431    };
432    (UserProperty, $properties:expr, $reader:expr, $property_id:expr) => {
433        let user_property = crate::v5::PropertyValue::decode_user_property($reader).await?;
434        $properties.user_properties.push(user_property);
435    };
436    (MaximumPacketSize, $properties:expr, $reader:expr, $property_id:expr) => {
437        crate::v5::PropertyValue::decode_u32(
438            $reader,
439            $property_id,
440            &mut $properties.max_packet_size,
441        )
442        .await?;
443    };
444    (WildcardSubscriptionAvailable, $properties:expr, $reader:expr, $property_id:expr) => {
445        crate::v5::PropertyValue::decode_bool(
446            $reader,
447            $property_id,
448            &mut $properties.wildcard_subscription_available,
449        )
450        .await?;
451    };
452    (SubscriptionIdentifierAvailable, $properties:expr, $reader:expr, $property_id:expr) => {
453        crate::v5::PropertyValue::decode_bool(
454            $reader,
455            $property_id,
456            &mut $properties.subscription_id_available,
457        )
458        .await?;
459    };
460    (SharedSubscriptionAvailable, $properties:expr, $reader:expr, $property_id:expr) => {
461        crate::v5::PropertyValue::decode_bool(
462            $reader,
463            $property_id,
464            &mut $properties.shared_subscription_available,
465        )
466        .await?;
467    };
468}
469
470macro_rules! decode_properties {
471    (LastWill, $properties:expr, $reader:expr, $($t:ident,)*) => {
472        let (property_len, _bytes) = crate::decode_var_int($reader).await?;
473        let mut len = 0;
474        while property_len as usize > len {
475            let property_id = crate::v5::PropertyId::from_u8(crate::read_u8($reader).await?)?;
476            match property_id {
477                $(
478                    crate::v5::PropertyId::$t => {
479                        crate::v5::decode_property!($t, $properties, $reader, property_id);
480                        crate::v5::encode_property_len!($t, $properties, len);
481                    }
482                )*
483                    crate::v5::PropertyId::UserProperty => {
484                        crate::v5::decode_property!(UserProperty, $properties, $reader, property_id);
485                        let last = $properties.user_properties.last().expect("user property exists");
486                        len += 1 + 4 + last.name.len() + last.value.len();
487                    }
488                    _ => return Err(crate::v5::ErrorV5::InvalidWillProperty(property_id)),
489            }
490        }
491        if property_len as usize != len {
492            return Err(crate::v5::ErrorV5::InvalidPropertyLength(property_len));
493        }
494    };
495    ($packet_type:expr, $properties:expr, $reader:expr, $($t:ident,)*) => {
496        let (property_len, _bytes) = crate::decode_var_int($reader).await?;
497        let mut len = 0;
498        while property_len as usize > len {
499            let property_id = crate::v5::PropertyId::from_u8(crate::read_u8($reader).await?)?;
500            match property_id {
501                $(
502                    crate::v5::PropertyId::$t => {
503                        crate::v5::decode_property!($t, $properties, $reader, property_id);
504                        crate::v5::encode_property_len!($t, $properties, len);
505                    }
506                )*
507                    crate::v5::PropertyId::UserProperty => {
508                        crate::v5::decode_property!(UserProperty, $properties, $reader, property_id);
509                        let last = $properties.user_properties.last().expect("user property exists");
510                        len += 1 + 4 + last.name.len() + last.value.len();
511                    }
512                _ => return Err(crate::v5::ErrorV5::InvalidProperty($packet_type, property_id)),
513            }
514        }
515        if property_len as usize != len {
516            return Err(crate::v5::ErrorV5::InvalidPropertyLength(property_len));
517        }
518    };
519}
520
521pub(crate) use decode_properties;
522pub(crate) use decode_property;
523
524macro_rules! encode_property {
525    (PayloadFormatIndicator, $properties:expr, $writer: expr) => {
526        if let Some(value) = $properties.payload_is_utf8 {
527            crate::write_u8($writer, crate::v5::PropertyId::PayloadFormatIndicator as u8)?;
528            crate::write_u8($writer, u8::from(value))?;
529        }
530    };
531    (MessageExpiryInterval, $properties:expr, $writer: expr) => {
532        if let Some(value) = $properties.message_expiry_interval {
533            crate::write_u8($writer, crate::v5::PropertyId::MessageExpiryInterval as u8)?;
534            crate::write_u32($writer, value)?;
535        }
536    };
537    (ContentType, $properties:expr, $writer: expr) => {
538        if let Some(value) = $properties.content_type.as_ref() {
539            crate::write_u8($writer, crate::v5::PropertyId::ContentType as u8)?;
540            crate::write_bytes($writer, value.as_bytes())?;
541        }
542    };
543    (ResponseTopic, $properties:expr, $writer: expr) => {
544        if let Some(value) = $properties.response_topic.as_ref() {
545            crate::write_u8($writer, crate::v5::PropertyId::ResponseTopic as u8)?;
546            crate::write_bytes($writer, value.as_bytes())?;
547        }
548    };
549    (CorrelationData, $properties:expr, $writer: expr) => {
550        if let Some(value) = $properties.correlation_data.as_ref() {
551            crate::write_u8($writer, crate::v5::PropertyId::CorrelationData as u8)?;
552            crate::write_bytes($writer, value.as_ref())?;
553        }
554    };
555    (SubscriptionIdentifier, $properties:expr, $writer: expr) => {
556        if let Some(value) = $properties.subscription_id {
557            crate::write_u8($writer, crate::v5::PropertyId::SubscriptionIdentifier as u8)?;
558            crate::write_var_int($writer, value.value() as usize)?;
559        }
560    };
561    (SessionExpiryInterval, $properties:expr, $writer: expr) => {
562        if let Some(value) = $properties.session_expiry_interval {
563            crate::write_u8($writer, crate::v5::PropertyId::SessionExpiryInterval as u8)?;
564            crate::write_u32($writer, value)?;
565        }
566    };
567    (AssignedClientIdentifier, $properties:expr, $writer: expr) => {
568        if let Some(value) = $properties.assigned_client_id.as_ref() {
569            crate::write_u8(
570                $writer,
571                crate::v5::PropertyId::AssignedClientIdentifier as u8,
572            )?;
573            crate::write_bytes($writer, value.as_bytes())?;
574        }
575    };
576    (ServerKeepAlive, $properties:expr, $writer: expr) => {
577        if let Some(value) = $properties.server_keep_alive {
578            crate::write_u8($writer, crate::v5::PropertyId::ServerKeepAlive as u8)?;
579            crate::write_u16($writer, value)?;
580        }
581    };
582    (AuthenticationMethod, $properties:expr, $writer: expr) => {
583        if let Some(value) = $properties.auth_method.as_ref() {
584            crate::write_u8($writer, crate::v5::PropertyId::AuthenticationMethod as u8)?;
585            crate::write_bytes($writer, value.as_bytes())?;
586        }
587    };
588    (AuthenticationData, $properties:expr, $writer: expr) => {
589        if let Some(value) = $properties.auth_data.as_ref() {
590            crate::write_u8($writer, crate::v5::PropertyId::AuthenticationData as u8)?;
591            crate::write_bytes($writer, value.as_ref())?;
592        }
593    };
594    (RequestProblemInformation, $properties:expr, $writer: expr) => {
595        if let Some(value) = $properties.request_problem_info {
596            crate::write_u8(
597                $writer,
598                crate::v5::PropertyId::RequestProblemInformation as u8,
599            )?;
600            crate::write_u8($writer, u8::from(value))?;
601        }
602    };
603    (WillDelayInterval, $properties:expr, $writer: expr) => {
604        if let Some(value) = $properties.delay_interval {
605            crate::write_u8($writer, crate::v5::PropertyId::WillDelayInterval as u8)?;
606            crate::write_u32($writer, value)?;
607        }
608    };
609    (RequestResponseInformation, $properties:expr, $writer: expr) => {
610        if let Some(value) = $properties.request_response_info {
611            crate::write_u8(
612                $writer,
613                crate::v5::PropertyId::RequestResponseInformation as u8,
614            )?;
615            crate::write_u8($writer, u8::from(value))?;
616        }
617    };
618    (ResponseInformation, $properties:expr, $writer: expr) => {
619        if let Some(value) = $properties.response_info.as_ref() {
620            crate::write_u8($writer, crate::v5::PropertyId::ResponseInformation as u8)?;
621            crate::write_bytes($writer, value.as_bytes())?;
622        }
623    };
624    (ServerReference, $properties:expr, $writer: expr) => {
625        if let Some(value) = $properties.server_reference.as_ref() {
626            crate::write_u8($writer, crate::v5::PropertyId::ServerReference as u8)?;
627            crate::write_bytes($writer, value.as_bytes())?;
628        }
629    };
630    (ReasonString, $properties:expr, $writer: expr) => {
631        if let Some(value) = $properties.reason_string.as_ref() {
632            crate::write_u8($writer, crate::v5::PropertyId::ReasonString as u8)?;
633            crate::write_bytes($writer, value.as_bytes())?;
634        }
635    };
636    (ReceiveMaximum, $properties:expr, $writer: expr) => {
637        if let Some(value) = $properties.receive_max {
638            crate::write_u8($writer, crate::v5::PropertyId::ReceiveMaximum as u8)?;
639            crate::write_u16($writer, value)?;
640        }
641    };
642    (TopicAliasMaximum, $properties:expr, $writer: expr) => {
643        if let Some(value) = $properties.topic_alias_max {
644            crate::write_u8($writer, crate::v5::PropertyId::TopicAliasMaximum as u8)?;
645            crate::write_u16($writer, value)?;
646        }
647    };
648    (TopicAlias, $properties:expr, $writer: expr) => {
649        if let Some(value) = $properties.topic_alias {
650            crate::write_u8($writer, crate::v5::PropertyId::TopicAlias as u8)?;
651            crate::write_u16($writer, value)?;
652        }
653    };
654    (MaximumQoS, $properties:expr, $writer: expr) => {
655        if let Some(value) = $properties.max_qos {
656            crate::write_u8($writer, crate::v5::PropertyId::MaximumQoS as u8)?;
657            crate::write_u8($writer, value as u8)?;
658        }
659    };
660    (RetainAvailable, $properties:expr, $writer: expr) => {
661        if let Some(value) = $properties.retain_available {
662            crate::write_u8($writer, crate::v5::PropertyId::RetainAvailable as u8)?;
663            crate::write_u8($writer, u8::from(value))?;
664        }
665    };
666    (MaximumPacketSize, $properties:expr, $writer: expr) => {
667        if let Some(value) = $properties.max_packet_size {
668            crate::write_u8($writer, crate::v5::PropertyId::MaximumPacketSize as u8)?;
669            crate::write_u32($writer, value)?;
670        }
671    };
672    (WildcardSubscriptionAvailable, $properties:expr, $writer: expr) => {
673        if let Some(value) = $properties.wildcard_subscription_available {
674            crate::write_u8(
675                $writer,
676                crate::v5::PropertyId::WildcardSubscriptionAvailable as u8,
677            )?;
678            crate::write_u8($writer, u8::from(value))?;
679        }
680    };
681    (SubscriptionIdentifierAvailable, $properties:expr, $writer: expr) => {
682        if let Some(value) = $properties.subscription_id_available {
683            crate::write_u8(
684                $writer,
685                crate::v5::PropertyId::SubscriptionIdentifierAvailable as u8,
686            )?;
687            crate::write_u8($writer, u8::from(value))?;
688        }
689    };
690    (SharedSubscriptionAvailable, $properties:expr, $writer: expr) => {
691        if let Some(value) = $properties.shared_subscription_available {
692            crate::write_u8(
693                $writer,
694                crate::v5::PropertyId::SharedSubscriptionAvailable as u8,
695            )?;
696            crate::write_u8($writer, u8::from(value))?;
697        }
698    };
699}
700
701macro_rules! encode_properties {
702    ($properties:expr, $writer:expr) => {
703        let property_len = $properties.user_properties.len() + $properties
704            .user_properties
705            .iter()
706            .map(|property| 4 + property.name.len() + property.value.len())
707            .sum::<usize>();
708        crate::write_var_int($writer, property_len)?;
709        for UserProperty { name, value } in $properties.user_properties.iter() {
710            crate::write_u8($writer, crate::v5::PropertyId::UserProperty as u8)?;
711            crate::write_bytes($writer, name.as_bytes())?;
712            crate::write_bytes($writer, value.as_bytes())?;
713        }
714    };
715    ($properties:expr, $writer:expr, $($t:ident,)+) => {
716        let mut property_len = $properties.user_properties.len() + $properties
717            .user_properties
718            .iter()
719            .map(|property| 4 + property.name.len() + property.value.len())
720            .sum::<usize>();
721        $(
722            crate::v5::encode_property_len!($t, $properties, property_len);
723        )+
724
725            crate::write_var_int($writer, property_len)?;
726        $(
727            crate::v5::encode_property!($t, $properties, $writer);
728        )*
729
730            for UserProperty { name, value } in $properties.user_properties.iter() {
731                crate::write_u8($writer, crate::v5::PropertyId::UserProperty as u8)?;
732                crate::write_bytes($writer, name.as_bytes())?;
733                crate::write_bytes($writer, value.as_bytes())?;
734            }
735    };
736}
737
738pub(crate) use encode_properties;
739pub(crate) use encode_property;
740
741macro_rules! encode_property_len {
742    (PayloadFormatIndicator, $properties:expr, $property_len:expr) => {
743        if $properties.payload_is_utf8.is_some() {
744            $property_len += 1 + 1;
745        }
746    };
747    (MessageExpiryInterval, $properties:expr, $property_len:expr) => {
748        if $properties.message_expiry_interval.is_some() {
749            $property_len += 1 + 4;
750        }
751    };
752    (ContentType, $properties:expr, $property_len:expr) => {
753        if let Some(value) = $properties.content_type.as_ref() {
754            $property_len += 1 + 2 + value.len();
755        }
756    };
757    (ResponseTopic, $properties:expr, $property_len:expr) => {
758        if let Some(value) = $properties.response_topic.as_ref() {
759            $property_len += 1 + 2 + value.len();
760        }
761    };
762    (CorrelationData, $properties:expr, $property_len:expr) => {
763        if let Some(value) = $properties.correlation_data.as_ref() {
764            $property_len += 1 + 2 + value.len();
765        }
766    };
767    (SubscriptionIdentifier, $properties:expr, $property_len:expr) => {
768        if let Some(value) = $properties.subscription_id {
769            $property_len += 1 + crate::var_int_len(value.value() as usize)
770                .expect("subscription id exceed 268,435,455");
771        }
772    };
773    (SessionExpiryInterval, $properties:expr, $property_len:expr) => {
774        if $properties.session_expiry_interval.is_some() {
775            $property_len += 1 + 4;
776        }
777    };
778    (AssignedClientIdentifier, $properties:expr, $property_len:expr) => {
779        if let Some(value) = $properties.assigned_client_id.as_ref() {
780            $property_len += 1 + 2 + value.len();
781        }
782    };
783    (ServerKeepAlive, $properties:expr, $property_len:expr) => {
784        if $properties.server_keep_alive.is_some() {
785            $property_len += 1 + 2;
786        }
787    };
788    (AuthenticationMethod, $properties:expr, $property_len:expr) => {
789        if let Some(value) = $properties.auth_method.as_ref() {
790            $property_len += 1 + 2 + value.len();
791        }
792    };
793    (AuthenticationData, $properties:expr, $property_len:expr) => {
794        if let Some(value) = $properties.auth_data.as_ref() {
795            $property_len += 1 + 2 + value.len();
796        }
797    };
798    (RequestProblemInformation, $properties:expr, $property_len:expr) => {
799        if $properties.request_problem_info.is_some() {
800            $property_len += 1 + 1;
801        }
802    };
803    (WillDelayInterval, $properties:expr, $property_len:expr) => {
804        if $properties.delay_interval.is_some() {
805            $property_len += 1 + 4;
806        }
807    };
808    (RequestResponseInformation, $properties:expr, $property_len:expr) => {
809        if $properties.request_response_info.is_some() {
810            $property_len += 1 + 1;
811        }
812    };
813    (ResponseInformation, $properties:expr, $property_len:expr) => {
814        if let Some(value) = $properties.response_info.as_ref() {
815            $property_len += 1 + 2 + value.len();
816        }
817    };
818    (ServerReference, $properties:expr, $property_len:expr) => {
819        if let Some(value) = $properties.server_reference.as_ref() {
820            $property_len += 1 + 2 + value.len();
821        }
822    };
823    (ReasonString, $properties:expr, $property_len:expr) => {
824        if let Some(value) = $properties.reason_string.as_ref() {
825            $property_len += 1 + 2 + value.len();
826        }
827    };
828    (ReceiveMaximum, $properties:expr, $property_len:expr) => {
829        if $properties.receive_max.is_some() {
830            $property_len += 1 + 2;
831        }
832    };
833    (TopicAliasMaximum, $properties:expr, $property_len:expr) => {
834        if $properties.topic_alias_max.is_some() {
835            $property_len += 1 + 2;
836        }
837    };
838    (TopicAlias, $properties:expr, $property_len:expr) => {
839        if $properties.topic_alias.is_some() {
840            $property_len += 1 + 2;
841        }
842    };
843    (MaximumQoS, $properties:expr, $property_len:expr) => {
844        if $properties.max_qos.is_some() {
845            $property_len += 1 + 1;
846        }
847    };
848    (RetainAvailable, $properties:expr, $property_len:expr) => {
849        if $properties.retain_available.is_some() {
850            $property_len += 1 + 1;
851        }
852    };
853    (MaximumPacketSize, $properties:expr, $property_len:expr) => {
854        if $properties.max_packet_size.is_some() {
855            $property_len += 1 + 4;
856        }
857    };
858    (WildcardSubscriptionAvailable, $properties:expr, $property_len:expr) => {
859        if $properties.wildcard_subscription_available.is_some() {
860            $property_len += 1 + 1;
861        }
862    };
863    (SubscriptionIdentifierAvailable, $properties:expr, $property_len:expr) => {
864        if $properties.subscription_id_available.is_some() {
865            $property_len += 1 + 1;
866        }
867    };
868    (SharedSubscriptionAvailable, $properties:expr, $property_len:expr) => {
869        if $properties.shared_subscription_available.is_some() {
870            $property_len += 1 + 1;
871        }
872    };
873}
874
875macro_rules! encode_properties_len {
876    ($properties:expr, $len:expr) => {
877        // Every properties have user property
878        let property_len: usize = $properties.user_properties.len() + $properties
879            .user_properties
880            .iter()
881            .map(|property| 4 + property.name.len() + property.value.len())
882            .sum::<usize>();
883        $len += property_len + crate::var_int_len(property_len).expect("total properties length exceed 268,435,455");
884    };
885    ($properties:expr, $len:expr, $($t:ident,)+) => {
886        // Every properties have user property
887        let mut property_len: usize = $properties.user_properties.len() + $properties
888            .user_properties
889            .iter()
890            .map(|property| 4 + property.name.len() + property.value.len())
891            .sum::<usize>();
892        $(
893            crate::v5::encode_property_len!($t, $properties, property_len);
894        )+
895
896            $len += property_len + crate::var_int_len(property_len).expect("total properties length exceed 268,435,455");
897    };
898}
899
900pub(crate) use encode_properties_len;
901pub(crate) use encode_property_len;