mqtt_proto/v5/
types.rs

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