mqtt_protocol_core/mqtt/packet/
property.rs

1// MIT License
2//
3// Copyright (c) 2025 Takatoshi Kondo
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22
23use crate::mqtt::packet::escape_binary_json_string;
24use crate::mqtt::packet::mqtt_binary::MqttBinary;
25use crate::mqtt::packet::mqtt_string::MqttString;
26use crate::mqtt::packet::DecodeResult;
27use crate::mqtt::packet::VariableByteInteger;
28use crate::mqtt::result_code::MqttError;
29use alloc::{string::String, vec::Vec};
30use core::convert::{TryFrom, TryInto};
31use core::fmt;
32use num_enum::TryFromPrimitive;
33use serde::ser::SerializeStruct;
34use serde::ser::Serializer;
35use serde::{Deserialize, Serialize};
36#[cfg(feature = "std")]
37use std::io::IoSlice;
38
39/// MQTT v5.0 Property Identifiers
40///
41/// This enum represents all property identifiers defined in the MQTT v5.0 specification.
42/// Properties are used to extend MQTT packets with additional metadata and control information.
43///
44/// Each property has a unique identifier (1-42) and is associated with specific packet types.
45/// Properties provide enhanced functionality such as message expiry, user properties,
46/// authentication data, and various server capabilities.
47///
48/// # Specification Reference
49///
50/// See [MQTT v5.0 Properties](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901029)
51/// for detailed information about each property.
52///
53/// # Examples
54///
55/// ```ignore
56/// use mqtt_protocol_core::mqtt;
57///
58/// let property_id = mqtt::packet::PropertyId::MessageExpiryInterval;
59/// assert_eq!(property_id.as_u8(), 2);
60/// assert_eq!(property_id.as_str(), "message_expiry_interval");
61/// ```
62#[derive(Deserialize, PartialEq, Eq, Copy, Clone, TryFromPrimitive)]
63#[cfg_attr(feature = "defmt", derive(defmt::Format))]
64#[repr(u8)]
65pub enum PropertyId {
66    /// Indicates the format of the payload in PUBLISH packets (0=binary, 1=UTF-8)
67    PayloadFormatIndicator = 1,
68    /// Message expiry interval in seconds
69    MessageExpiryInterval = 2,
70    /// Content type of the application message
71    ContentType = 3,
72    /// Topic name for response messages
73    ResponseTopic = 8,
74    /// Correlation data for request/response messaging
75    CorrelationData = 9,
76    /// Subscription identifier for matching subscriptions
77    SubscriptionIdentifier = 11,
78    /// Session expiry interval in seconds
79    SessionExpiryInterval = 17,
80    /// Client identifier assigned by the server
81    AssignedClientIdentifier = 18,
82    /// Keep alive time assigned by the server
83    ServerKeepAlive = 19,
84    /// Authentication method name
85    AuthenticationMethod = 21,
86    /// Authentication data
87    AuthenticationData = 22,
88    /// Request problem information flag
89    RequestProblemInformation = 23,
90    /// Will delay interval in seconds
91    WillDelayInterval = 24,
92    /// Request response information flag
93    RequestResponseInformation = 25,
94    /// Response information string
95    ResponseInformation = 26,
96    /// Server reference for redirection
97    ServerReference = 28,
98    /// Human readable reason string
99    ReasonString = 31,
100    /// Maximum number of concurrent PUBLISH packets
101    ReceiveMaximum = 33,
102    /// Maximum topic alias value
103    TopicAliasMaximum = 34,
104    /// Topic alias value
105    TopicAlias = 35,
106    /// Maximum QoS level supported
107    MaximumQos = 36,
108    /// Retain availability flag
109    RetainAvailable = 37,
110    /// User-defined property key-value pair
111    UserProperty = 38,
112    /// Maximum packet size
113    MaximumPacketSize = 39,
114    /// Wildcard subscription availability flag
115    WildcardSubscriptionAvailable = 40,
116    /// Subscription identifier availability flag
117    SubscriptionIdentifierAvailable = 41,
118    /// Shared subscription availability flag
119    SharedSubscriptionAvailable = 42,
120}
121
122impl PropertyId {
123    /// Get the numeric identifier of the property
124    ///
125    /// Returns the property identifier as defined in the MQTT v5.0 specification.
126    ///
127    /// # Examples
128    ///
129    /// ```ignore
130    /// use mqtt_protocol_core::mqtt;
131    ///
132    /// let prop = mqtt::packet::PropertyId::MessageExpiryInterval;
133    /// assert_eq!(prop.as_u8(), 2);
134    /// ```
135    pub fn as_u8(self) -> u8 {
136        self as u8
137    }
138
139    /// Get the string representation of the property identifier
140    ///
141    /// Returns a human-readable string name for the property, suitable for
142    /// serialization and debugging purposes.
143    ///
144    /// # Examples
145    ///
146    /// ```ignore
147    /// use mqtt_protocol_core::mqtt;
148    ///
149    /// let prop = mqtt::packet::PropertyId::ContentType;
150    /// assert_eq!(prop.as_str(), "content_type");
151    /// ```
152    pub fn as_str(&self) -> &'static str {
153        match self {
154            PropertyId::PayloadFormatIndicator => "payload_format_indicator",
155            PropertyId::MessageExpiryInterval => "message_expiry_interval",
156            PropertyId::ContentType => "content_type",
157            PropertyId::ResponseTopic => "response_topic",
158            PropertyId::CorrelationData => "correlation_data",
159            PropertyId::SubscriptionIdentifier => "subscription_identifier",
160            PropertyId::SessionExpiryInterval => "session_expiry_interval",
161            PropertyId::AssignedClientIdentifier => "assigned_client_identifier",
162            PropertyId::ServerKeepAlive => "server_keep_alive",
163            PropertyId::AuthenticationMethod => "authentication_method",
164            PropertyId::AuthenticationData => "authentication_data",
165            PropertyId::RequestProblemInformation => "request_problem_information",
166            PropertyId::WillDelayInterval => "will_delay_interval",
167            PropertyId::RequestResponseInformation => "request_response_information",
168            PropertyId::ResponseInformation => "response_information",
169            PropertyId::ServerReference => "server_reference",
170            PropertyId::ReasonString => "reason_string",
171            PropertyId::ReceiveMaximum => "receive_maximum",
172            PropertyId::TopicAliasMaximum => "topic_alias_maximum",
173            PropertyId::TopicAlias => "topic_alias",
174            PropertyId::MaximumQos => "maximum_qos",
175            PropertyId::RetainAvailable => "retain_available",
176            PropertyId::UserProperty => "user_property",
177            PropertyId::MaximumPacketSize => "maximum_packet_size",
178            PropertyId::WildcardSubscriptionAvailable => "wildcard_subscription_available",
179            PropertyId::SubscriptionIdentifierAvailable => "subscription_identifier_available",
180            PropertyId::SharedSubscriptionAvailable => "shared_subscription_available",
181        }
182    }
183}
184
185impl Serialize for PropertyId {
186    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
187    where
188        S: Serializer,
189    {
190        serializer.serialize_str(self.as_str())
191    }
192}
193
194impl fmt::Display for PropertyId {
195    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196        match serde_json::to_string(self) {
197            Ok(json) => write!(f, "{json}"),
198            Err(e) => write!(f, "{{\"error\": \"{e}\"}}"),
199        }
200    }
201}
202
203impl fmt::Debug for PropertyId {
204    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205        fmt::Display::fmt(self, f)
206    }
207}
208
209/// Payload Format Indicator values
210///
211/// Specifies the format of the payload in PUBLISH packets.
212/// This helps receivers interpret the payload data correctly.
213///
214/// # Specification Reference
215///
216/// See [Payload Format Indicator](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901111)
217/// in the MQTT v5.0 specification.
218///
219/// # Examples
220///
221/// ```ignore
222/// use mqtt_protocol_core::mqtt;
223///
224/// let format = mqtt::packet::PayloadFormat::String;
225/// assert_eq!(format as u8, 1);
226/// ```
227#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, TryFromPrimitive)]
228#[cfg_attr(feature = "defmt", derive(defmt::Format))]
229#[repr(u8)]
230pub enum PayloadFormat {
231    /// Payload is unspecified bytes (binary data)
232    Binary = 0,
233    /// Payload is UTF-8 encoded character data
234    String = 1,
235}
236impl fmt::Display for PayloadFormat {
237    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238        let s = match self {
239            PayloadFormat::Binary => "binary",
240            PayloadFormat::String => "string",
241        };
242        write!(f, "{s}")
243    }
244}
245
246/// Trait for calculating the encoded size of property values
247///
248/// This trait provides a method to determine how many bytes a property value
249/// will occupy when encoded according to the MQTT v5.0 specification.
250pub trait PropertySize {
251    /// Calculate the encoded size of the property value in bytes
252    ///
253    /// Returns the number of bytes required to encode this value in the MQTT wire format.
254    fn size(&self) -> usize;
255}
256
257/// Implementation of PropertySize for u8 values
258impl PropertySize for u8 {
259    fn size(&self) -> usize {
260        1
261    }
262}
263
264/// Implementation of PropertySize for u16 values (big-endian encoding)
265impl PropertySize for u16 {
266    fn size(&self) -> usize {
267        2
268    }
269}
270
271/// Implementation of PropertySize for u32 values (big-endian encoding)
272impl PropertySize for u32 {
273    fn size(&self) -> usize {
274        4
275    }
276}
277/// Implementation of PropertySize for String values (UTF-8 string with 2-byte length prefix)
278impl PropertySize for String {
279    fn size(&self) -> usize {
280        2 + self.len()
281    }
282}
283
284/// Implementation of PropertySize for `Vec<u8>` values (binary data with 2-byte length prefix)
285impl PropertySize for Vec<u8> {
286    fn size(&self) -> usize {
287        2 + self.len()
288    }
289}
290
291/// Implementation of PropertySize for VariableByteInteger values
292/// Variable byte integers use 1-4 bytes depending on the value
293impl PropertySize for VariableByteInteger {
294    fn size(&self) -> usize {
295        match self.to_u32() {
296            0..=0x7F => 1,
297            0x80..=0x3FFF => 2,
298            0x4000..=0x1F_FFFF => 3,
299            _ => 4,
300        }
301    }
302}
303
304macro_rules! mqtt_property_common {
305    ($name:ident, $id:expr, $ty:ty) => {
306        #[derive(Debug, PartialEq, Eq, Clone)]
307        pub struct $name {
308            id_bytes: [u8; 1],
309            value: $ty,
310        }
311
312        impl $name {
313            /// Returns the PropertyId of this property.
314            ///
315            /// # Returns
316            ///
317            /// The PropertyId enum value.
318            ///
319            /// # Examples
320            ///
321            /// ```ignore
322            /// let prop = Property::new(...);
323            /// let id = prop.id();
324            /// ```
325            pub fn id(&self) -> PropertyId {
326                $id
327            }
328        }
329
330        impl From<$name> for Property {
331            fn from(v: $name) -> Self {
332                Property::$name(v)
333            }
334        }
335    };
336}
337
338macro_rules! mqtt_property_binary {
339    ($name:ident, $id:expr) => {
340        mqtt_property_common!($name, $id, MqttBinary);
341
342        impl serde::Serialize for $name {
343            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
344            where
345                S: serde::Serializer,
346            {
347                let escaped = escape_binary_json_string(self.val());
348
349                let mut state = serializer.serialize_struct(stringify!($name), 2)?;
350                state.serialize_field("id", &($id as u8))?;
351                state.serialize_field("val", &escaped)?;
352                state.end()
353            }
354        }
355
356        impl $name {
357            /// Creates a new binary property with the given value.
358            ///
359            /// # Parameters
360            ///
361            /// * `v` - The binary value to set (can be any type that converts to bytes)
362            ///
363            /// # Returns
364            ///
365            /// * `Ok(Self)` - Successfully created property
366            /// * `Err(MqttError)` - If the binary data is invalid or too large
367            ///
368            /// # Examples
369            ///
370            /// ```ignore
371            /// let prop = CorrelationData::new(b"correlation-123").unwrap();
372            /// ```
373            pub fn new<T>(v: T) -> Result<Self, MqttError>
374            where
375                T: TryInto<MqttBinary, Error = MqttError>,
376            {
377                let binary = v.try_into()?;
378
379                Ok(Self {
380                    id_bytes: [$id as u8],
381                    value: binary,
382                })
383            }
384
385            /// Parses a binary property from the given byte slice.
386            ///
387            /// # Parameters
388            ///
389            /// * `bytes` - The byte slice to parse from
390            ///
391            /// # Returns
392            ///
393            /// * `Ok((Self, usize))` - The parsed property and number of bytes consumed
394            /// * `Err(MqttError)` - If parsing fails
395            ///
396            /// # Examples
397            ///
398            /// ```ignore
399            /// let data = &[0x00, 0x05, b'h', b'e', b'l', b'l', b'o'];
400            /// let (prop, consumed) = CorrelationData::parse(data).unwrap();
401            /// assert_eq!(consumed, 7);
402            /// ```
403            pub fn parse(bytes: &[u8]) -> Result<(Self, usize), MqttError> {
404                let (mqtt_binary, consumed) = MqttBinary::decode(bytes)?;
405                Ok((
406                    Self {
407                        id_bytes: [$id as u8],
408                        value: mqtt_binary,
409                    },
410                    consumed,
411                ))
412            }
413
414            /// Converts the property to I/O slices for efficient transmission.
415            ///
416            /// # Returns
417            ///
418            /// A vector of I/O slices containing the property data.
419            ///
420            /// # Examples
421            ///
422            /// ```ignore
423            /// let prop = CorrelationData::new(b"data").unwrap();
424            /// let buffers = prop.to_buffers();
425            /// ```
426            #[cfg(feature = "std")]
427            pub fn to_buffers(&self) -> Vec<IoSlice<'_>> {
428                let mut result = vec![IoSlice::new(&self.id_bytes)];
429                let mut binary_bufs = self.value.to_buffers();
430                result.append(&mut binary_bufs);
431                result
432            }
433
434            /// Converts the property to a continuous buffer.
435            ///
436            /// # Returns
437            ///
438            /// A byte vector containing the complete property data.
439            ///
440            /// # Examples
441            ///
442            /// ```ignore
443            /// let prop = CorrelationData::new(b"data").unwrap();
444            /// let buffer = prop.to_continuous_buffer();
445            /// ```
446            /// Converts the property to a continuous buffer.
447            ///
448            /// # Returns
449            ///
450            /// A byte vector containing the complete property data.
451            ///
452            /// # Examples
453            ///
454            /// ```ignore
455            /// let prop = Property::new(...).unwrap();
456            /// let buffer = prop.to_continuous_buffer();
457            /// ```
458            pub fn to_continuous_buffer(&self) -> Vec<u8> {
459                let mut buf = Vec::new();
460                buf.extend_from_slice(&self.id_bytes);
461                buf.append(&mut self.value.to_continuous_buffer());
462                buf
463            }
464
465            /// Returns the binary value of this property.
466            ///
467            /// # Returns
468            ///
469            /// A reference to the binary data as a byte slice.
470            ///
471            /// # Examples
472            ///
473            /// ```ignore
474            /// let prop = CorrelationData::new(b"hello").unwrap();
475            /// assert_eq!(prop.val(), b"hello");
476            /// ```
477            pub fn val(&self) -> &[u8] {
478                self.value.as_slice()
479            }
480
481            /// Returns the total size of this property in bytes.
482            ///
483            /// This includes the property ID (1 byte) plus the binary data size.
484            ///
485            /// # Returns
486            ///
487            /// The total size in bytes.
488            ///
489            /// # Examples
490            ///
491            /// ```ignore
492            /// let prop = CorrelationData::new(b"hello").unwrap();
493            /// assert_eq!(prop.size(), 8); // 1 (ID) + 2 (length) + 5 (data)
494            /// ```
495            pub fn size(&self) -> usize {
496                1 + self.value.size() // ID + MqttBinary size
497            }
498        }
499
500        impl fmt::Display for $name {
501            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
502                match escape_binary_json_string(self.val()) {
503                    Some(escaped) => write!(
504                        f,
505                        "{{\"id\": \"{}\", \"value\": \"{}\"}}",
506                        self.id(),
507                        escaped
508                    ),
509                    None => write!(
510                        f,
511                        "{{\"id\": \"{}\", \"value\": \"{:?}\"}}",
512                        self.id(),
513                        self.val()
514                    ),
515                }
516            }
517        }
518    };
519}
520
521macro_rules! mqtt_property_string {
522    ($name:ident, $id:expr) => {
523        mqtt_property_common!($name, $id, MqttString);
524
525        impl serde::Serialize for $name {
526            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
527            where
528                S: serde::Serializer,
529            {
530                let mut s = serializer.serialize_struct(stringify!($name), 2)?;
531                s.serialize_field("id", &($id as u8))?;
532                s.serialize_field("val", self.val())?;
533                s.end()
534            }
535        }
536
537        impl $name {
538            /// Creates a new string property with the given value.
539            ///
540            /// # Parameters
541            ///
542            /// * `s` - The string value to set
543            ///
544            /// # Returns
545            ///
546            /// * `Ok(Self)` - Successfully created property
547            /// * `Err(MqttError)` - If the string is invalid or too long
548            ///
549            /// # Examples
550            ///
551            /// ```ignore
552            /// let prop = ContentType::new("application/json").unwrap();
553            /// ```
554            pub fn new<T>(s: T) -> Result<Self, MqttError>
555            where
556                T: TryInto<MqttString, Error = MqttError>,
557            {
558                let value = s.try_into()?;
559
560                Ok(Self {
561                    id_bytes: [$id as u8],
562                    value,
563                })
564            }
565
566            /// Parses a string property from the given byte slice.
567            ///
568            /// # Parameters
569            ///
570            /// * `bytes` - The byte slice to parse from
571            ///
572            /// # Returns
573            ///
574            /// * `Ok((Self, usize))` - The parsed property and number of bytes consumed
575            /// * `Err(MqttError)` - If parsing fails
576            ///
577            /// # Examples
578            ///
579            /// ```ignore
580            /// let data = &[0x00, 0x05, b'h', b'e', b'l', b'l', b'o'];
581            /// let (prop, consumed) = ContentType::parse(data).unwrap();
582            /// assert_eq!(consumed, 7);
583            /// ```
584            pub fn parse(bytes: &[u8]) -> Result<(Self, usize), MqttError> {
585                let (mqtt_string, consumed) = MqttString::decode(bytes)?;
586                Ok((
587                    Self {
588                        id_bytes: [$id as u8],
589                        value: mqtt_string,
590                    },
591                    consumed,
592                ))
593            }
594
595            /// Converts the property to I/O slices for efficient transmission.
596            ///
597            /// # Returns
598            ///
599            /// A vector of I/O slices containing the property data.
600            ///
601            /// # Examples
602            ///
603            /// ```ignore
604            /// let prop = ContentType::new("text/plain").unwrap();
605            /// let buffers = prop.to_buffers();
606            /// ```
607            #[cfg(feature = "std")]
608            pub fn to_buffers(&self) -> Vec<IoSlice<'_>> {
609                let mut result = vec![IoSlice::new(&self.id_bytes)];
610                let mut string_bufs = self.value.to_buffers();
611                result.append(&mut string_bufs);
612                result
613            }
614
615            /// Converts the property to a continuous buffer.
616            ///
617            /// # Returns
618            ///
619            /// A byte vector containing the complete property data.
620            ///
621            /// # Examples
622            ///
623            /// ```ignore
624            /// let prop = Property::new(...).unwrap();
625            /// let buffer = prop.to_continuous_buffer();
626            /// ```
627            pub fn to_continuous_buffer(&self) -> Vec<u8> {
628                let mut buf = Vec::new();
629                buf.extend_from_slice(&self.id_bytes);
630                buf.append(&mut self.value.to_continuous_buffer());
631                buf
632            }
633
634            /// Returns the string value of this property.
635            ///
636            /// # Returns
637            ///
638            /// A reference to the string value.
639            ///
640            /// # Examples
641            ///
642            /// ```ignore
643            /// let prop = ContentType::new("application/json").unwrap();
644            /// assert_eq!(prop.val(), "application/json");
645            /// ```
646            pub fn val(&self) -> &str {
647                self.value.as_str()
648            }
649
650            /// Returns the total size of this property in bytes.
651            ///
652            /// This includes the property ID (1 byte) plus the string data size.
653            ///
654            /// # Returns
655            ///
656            /// The total size in bytes.
657            ///
658            /// # Examples
659            ///
660            /// ```ignore
661            /// let prop = ContentType::new("hello").unwrap();
662            /// assert_eq!(prop.size(), 8); // 1 (ID) + 2 (length) + 5 (data)
663            /// ```
664            pub fn size(&self) -> usize {
665                1 + self.value.size() // ID + MqttString size
666            }
667        }
668
669        impl fmt::Display for $name {
670            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
671                write!(
672                    f,
673                    "{{\"id\": \"{}\", \"value\": \"{}\"}}",
674                    self.id(),
675                    self.val()
676                )
677            }
678        }
679    };
680}
681
682macro_rules! mqtt_property_string_pair {
683    ($name:ident, $id:expr) => {
684        mqtt_property_common!($name, $id, (MqttString, MqttString));
685
686        impl serde::Serialize for $name {
687            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
688            where
689                S: serde::Serializer,
690            {
691                let mut s = serializer.serialize_struct(stringify!($name), 3)?;
692                s.serialize_field("id", &($id as u8))?;
693                s.serialize_field("key", self.key())?;
694                s.serialize_field("val", self.val())?;
695                s.end()
696            }
697        }
698
699        impl $name {
700            /// Creates a new string pair property with the given key and value.
701            ///
702            /// # Parameters
703            ///
704            /// * `key` - The key string
705            /// * `val` - The value string
706            ///
707            /// # Returns
708            ///
709            /// * `Ok(Self)` - Successfully created property
710            /// * `Err(MqttError)` - If either string is invalid or too long
711            ///
712            /// # Examples
713            ///
714            /// ```ignore
715            /// let prop = UserProperty::new("name", "value").unwrap();
716            /// ```
717            pub fn new<K, V>(key: K, val: V) -> Result<Self, MqttError>
718            where
719                K: TryInto<MqttString, Error = MqttError>,
720                V: TryInto<MqttString, Error = MqttError>,
721            {
722                let key_mqtt = key.try_into()?;
723                let val_mqtt = val.try_into()?;
724
725                Ok(Self {
726                    id_bytes: [$id as u8],
727                    value: (key_mqtt, val_mqtt),
728                })
729            }
730
731            /// Parses a string pair property from the given byte slice.
732            ///
733            /// # Parameters
734            ///
735            /// * `bytes` - The byte slice to parse from
736            ///
737            /// # Returns
738            ///
739            /// * `Ok((Self, usize))` - The parsed property and number of bytes consumed
740            /// * `Err(MqttError)` - If parsing fails
741            ///
742            /// # Examples
743            ///
744            /// ```ignore
745            /// let data = &[0x00, 0x03, b'k', b'e', b'y', 0x00, 0x05, b'v', b'a', b'l', b'u', b'e'];
746            /// let (prop, consumed) = UserProperty::parse(data).unwrap();
747            /// assert_eq!(consumed, 12);
748            /// ```
749            pub fn parse(bytes: &[u8]) -> Result<(Self, usize), MqttError> {
750                let (key, key_consumed) = MqttString::decode(bytes)?;
751                let (val, val_consumed) = MqttString::decode(&bytes[key_consumed..])?;
752
753                Ok((
754                    Self {
755                        id_bytes: [$id as u8],
756                        value: (key, val),
757                    },
758                    key_consumed + val_consumed,
759                ))
760            }
761
762            /// Converts the property to I/O slices for efficient transmission.
763            ///
764            /// # Returns
765            ///
766            /// A vector of I/O slices containing the property data.
767            ///
768            /// # Examples
769            ///
770            /// ```ignore
771            /// let prop = UserProperty::new("name", "value").unwrap();
772            /// let buffers = prop.to_buffers();
773            /// ```
774            #[cfg(feature = "std")]
775            pub fn to_buffers(&self) -> Vec<IoSlice<'_>> {
776                let mut result = vec![IoSlice::new(&self.id_bytes)];
777                let mut key_bufs = self.value.0.to_buffers();
778                let mut val_bufs = self.value.1.to_buffers();
779
780                result.append(&mut key_bufs);
781                result.append(&mut val_bufs);
782                result
783            }
784
785            /// Converts the property to a continuous buffer.
786            ///
787            /// # Returns
788            ///
789            /// A byte vector containing the complete property data.
790            ///
791            /// # Examples
792            ///
793            /// ```ignore
794            /// let prop = UserProperty::new("key", "value").unwrap();
795            /// let buffer = prop.to_continuous_buffer();
796            /// ```
797            pub fn to_continuous_buffer(&self) -> Vec<u8> {
798                let mut buf = Vec::new();
799                buf.extend_from_slice(&self.id_bytes);
800                buf.append(&mut self.value.0.to_continuous_buffer());
801                buf.append(&mut self.value.1.to_continuous_buffer());
802                buf
803            }
804
805            /// Returns the key string of this property.
806            ///
807            /// # Returns
808            ///
809            /// A reference to the key string.
810            ///
811            /// # Examples
812            ///
813            /// ```ignore
814            /// let prop = UserProperty::new("name", "value").unwrap();
815            /// assert_eq!(prop.key(), "name");
816            /// ```
817            pub fn key(&self) -> &str {
818                self.value.0.as_str()
819            }
820
821            /// Returns the value string of this property.
822            ///
823            /// # Returns
824            ///
825            /// A reference to the value string.
826            ///
827            /// # Examples
828            ///
829            /// ```ignore
830            /// let prop = UserProperty::new("name", "value").unwrap();
831            /// assert_eq!(prop.val(), "value");
832            /// ```
833            pub fn val(&self) -> &str {
834                self.value.1.as_str()
835            }
836
837            /// Returns the total size of this property in bytes.
838            ///
839            /// This includes the property ID (1 byte) plus both key and value string sizes.
840            ///
841            /// # Returns
842            ///
843            /// The total size in bytes.
844            ///
845            /// # Examples
846            ///
847            /// ```ignore
848            /// let prop = UserProperty::new("key", "value").unwrap();
849            /// assert_eq!(prop.size(), 13); // 1 (ID) + 2 (key len) + 3 (key) + 2 (val len) + 5 (val)
850            /// ```
851            pub fn size(&self) -> usize {
852                1 + self.value.0.size() + self.value.1.size() // ID + key size + value size
853            }
854        }
855
856        impl fmt::Display for $name {
857            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
858                write!(
859                    f,
860                    "{{\"id\": \"{}\", \"key\": \"{}\", \"val\": \"{}\"}}",
861                    self.id(),
862                    self.key(),
863                    self.val()
864                )
865            }
866        }
867    };
868}
869
870macro_rules! mqtt_property_u8_custom_new {
871    ($name:ident, $id:expr, $validator:expr) => {
872        mqtt_property_common!($name, $id, [u8; 1]);
873
874        impl serde::Serialize for $name {
875            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
876            where
877                S: serde::Serializer,
878            {
879                let mut s = serializer.serialize_struct(stringify!($name), 2)?;
880                s.serialize_field("id", &($id as u8))?;
881                s.serialize_field("val", &self.val())?;
882                s.end()
883            }
884        }
885
886        impl $name {
887            /// Parses a u8 property from the given byte slice.
888            ///
889            /// # Parameters
890            ///
891            /// * `bytes` - The byte slice to parse from
892            ///
893            /// # Returns
894            ///
895            /// * `Ok((Self, usize))` - The parsed property and number of bytes consumed
896            /// * `Err(MqttError)` - If parsing fails or validation fails
897            ///
898            /// # Examples
899            ///
900            /// ```ignore
901            /// let data = &[42];
902            /// let (prop, consumed) = PayloadFormatIndicator::parse(data).unwrap();
903            /// assert_eq!(consumed, 1);
904            /// ```
905            pub fn parse(bytes: &[u8]) -> Result<(Self, usize), MqttError> {
906                if bytes.len() < 1 {
907                    return Err(MqttError::MalformedPacket);
908                }
909                if let Some(validator) = $validator {
910                    validator(bytes[0])?;
911                }
912                Ok((
913                    Self {
914                        id_bytes: [$id as u8],
915                        value: [bytes[0]],
916                    },
917                    1,
918                ))
919            }
920
921            /// Converts the property to I/O slices for efficient transmission.
922            ///
923            /// # Returns
924            ///
925            /// A vector of I/O slices containing the property data.
926            ///
927            /// # Examples
928            ///
929            /// ```ignore
930            /// let prop = PayloadFormatIndicator::new(1).unwrap();
931            /// let buffers = prop.to_buffers();
932            /// ```
933            #[cfg(feature = "std")]
934            pub fn to_buffers(&self) -> Vec<IoSlice<'_>> {
935                vec![IoSlice::new(&self.id_bytes), IoSlice::new(&self.value)]
936            }
937
938            /// Converts the property to a continuous buffer.
939            ///
940            /// # Returns
941            ///
942            /// A byte vector containing the complete property data.
943            ///
944            /// # Examples
945            ///
946            /// ```ignore
947            /// let prop = Property::new(...).unwrap();
948            /// let buffer = prop.to_continuous_buffer();
949            /// ```
950            pub fn to_continuous_buffer(&self) -> Vec<u8> {
951                let mut buf = Vec::new();
952                buf.extend_from_slice(&self.id_bytes);
953                buf.extend_from_slice(&self.value);
954                buf
955            }
956
957            /// Returns the u8 value of this property.
958            ///
959            /// # Returns
960            ///
961            /// The u8 value.
962            ///
963            /// # Examples
964            ///
965            /// ```ignore
966            /// let prop = PayloadFormatIndicator::new(1).unwrap();
967            /// assert_eq!(prop.val(), 1);
968            /// ```
969            pub fn val(&self) -> u8 {
970                self.value[0]
971            }
972
973            /// Returns the total size of this property in bytes.
974            ///
975            /// This includes the property ID (1 byte) plus the u8 value (1 byte).
976            ///
977            /// # Returns
978            ///
979            /// The total size in bytes (always 2 for u8 properties).
980            ///
981            /// # Examples
982            ///
983            /// ```ignore
984            /// let prop = PayloadFormatIndicator::new(1).unwrap();
985            /// assert_eq!(prop.size(), 2);
986            /// ```
987            pub fn size(&self) -> usize {
988                1 + self.value.len()
989            }
990        }
991
992        impl fmt::Display for $name {
993            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
994                write!(
995                    f,
996                    "{{\"id\": \"{}\", \"value\": {}}}",
997                    self.id(),
998                    self.val()
999                )
1000            }
1001        }
1002    };
1003}
1004
1005macro_rules! mqtt_property_u8 {
1006    ($name:ident, $id:expr, $validator:expr) => {
1007        mqtt_property_u8_custom_new!($name, $id, $validator);
1008
1009        impl $name {
1010            /// Creates a new u8 property with the given value.
1011            ///
1012            /// # Parameters
1013            ///
1014            /// * `v` - The u8 value to set
1015            ///
1016            /// # Returns
1017            ///
1018            /// * `Ok(Self)` - Successfully created property
1019            /// * `Err(MqttError)` - If the value fails validation
1020            ///
1021            /// # Examples
1022            ///
1023            /// ```ignore
1024            /// let prop = PayloadFormatIndicator::new(1).unwrap();
1025            /// ```
1026            pub fn new(v: u8) -> Result<Self, MqttError> {
1027                if let Some(validator) = $validator {
1028                    validator(v)?;
1029                }
1030                Ok(Self {
1031                    id_bytes: [$id as u8],
1032                    value: [v],
1033                })
1034            }
1035        }
1036    };
1037}
1038
1039macro_rules! mqtt_property_u16 {
1040    ($name:ident, $id:expr, $validator:expr) => {
1041        mqtt_property_common!($name, $id, [u8; 2]);
1042
1043        impl serde::Serialize for $name {
1044            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1045            where
1046                S: serde::Serializer,
1047            {
1048                let mut s = serializer.serialize_struct(stringify!($name), 2)?;
1049                s.serialize_field("id", &($id as u8))?;
1050                s.serialize_field("val", &self.val())?;
1051                s.end()
1052            }
1053        }
1054
1055        impl $name {
1056            /// Creates a new u16 property with the given value.
1057            ///
1058            /// # Parameters
1059            ///
1060            /// * `v` - The u16 value to set
1061            ///
1062            /// # Returns
1063            ///
1064            /// * `Ok(Self)` - Successfully created property
1065            /// * `Err(MqttError)` - If the value fails validation
1066            ///
1067            /// # Examples
1068            ///
1069            /// ```ignore
1070            /// let prop = ServerKeepAlive::new(60).unwrap();
1071            /// ```
1072            pub fn new(v: u16) -> Result<Self, MqttError> {
1073                if let Some(validator) = $validator {
1074                    validator(v)?;
1075                }
1076                Ok(Self {
1077                    id_bytes: [$id as u8],
1078                    value: v.to_be_bytes(),
1079                })
1080            }
1081
1082            /// Parses a u16 property from the given byte slice.
1083            ///
1084            /// # Parameters
1085            ///
1086            /// * `bytes` - The byte slice to parse from
1087            ///
1088            /// # Returns
1089            ///
1090            /// * `Ok((Self, usize))` - The parsed property and number of bytes consumed
1091            /// * `Err(MqttError)` - If parsing fails or validation fails
1092            ///
1093            /// # Examples
1094            ///
1095            /// ```ignore
1096            /// let data = &[0x00, 0x3C]; // 60 in big-endian
1097            /// let (prop, consumed) = ServerKeepAlive::parse(data).unwrap();
1098            /// assert_eq!(consumed, 2);
1099            /// ```
1100            pub fn parse(bytes: &[u8]) -> Result<(Self, usize), MqttError> {
1101                if bytes.len() < 2 {
1102                    return Err(MqttError::MalformedPacket);
1103                }
1104                let v = u16::from_be_bytes([bytes[0], bytes[1]]);
1105                if let Some(validator) = $validator {
1106                    validator(v)?;
1107                }
1108                Ok((
1109                    Self {
1110                        id_bytes: [$id as u8],
1111                        value: bytes[..2].try_into().unwrap(),
1112                    },
1113                    2,
1114                ))
1115            }
1116
1117            /// Converts the property to I/O slices for efficient transmission.
1118            ///
1119            /// # Returns
1120            ///
1121            /// A vector of I/O slices containing the property data.
1122            ///
1123            /// # Examples
1124            ///
1125            /// ```ignore
1126            /// let prop = ServerKeepAlive::new(60).unwrap();
1127            /// let buffers = prop.to_buffers();
1128            /// ```
1129            #[cfg(feature = "std")]
1130            pub fn to_buffers(&self) -> Vec<IoSlice<'_>> {
1131                vec![IoSlice::new(&self.id_bytes), IoSlice::new(&self.value)]
1132            }
1133
1134            /// Converts the property to a continuous buffer.
1135            ///
1136            /// # Returns
1137            ///
1138            /// A byte vector containing the complete property data.
1139            ///
1140            /// # Examples
1141            ///
1142            /// ```ignore
1143            /// let prop = Property::new(...).unwrap();
1144            /// let buffer = prop.to_continuous_buffer();
1145            /// ```
1146            pub fn to_continuous_buffer(&self) -> Vec<u8> {
1147                let mut buf = Vec::new();
1148                buf.extend_from_slice(&self.id_bytes);
1149                buf.extend_from_slice(&self.value);
1150                buf
1151            }
1152
1153            /// Returns the u16 value of this property.
1154            ///
1155            /// # Returns
1156            ///
1157            /// The u16 value.
1158            ///
1159            /// # Examples
1160            ///
1161            /// ```ignore
1162            /// let prop = ServerKeepAlive::new(60).unwrap();
1163            /// assert_eq!(prop.val(), 60);
1164            /// ```
1165            pub fn val(&self) -> u16 {
1166                u16::from_be_bytes([self.value[0], self.value[1]])
1167            }
1168
1169            /// Returns the total size of this property in bytes.
1170            ///
1171            /// This includes the property ID (1 byte) plus the u16 value (2 bytes).
1172            ///
1173            /// # Returns
1174            ///
1175            /// The total size in bytes (always 3 for u16 properties).
1176            ///
1177            /// # Examples
1178            ///
1179            /// ```ignore
1180            /// let prop = ServerKeepAlive::new(60).unwrap();
1181            /// assert_eq!(prop.size(), 3);
1182            /// ```
1183            pub fn size(&self) -> usize {
1184                1 + self.value.len()
1185            }
1186        }
1187
1188        impl fmt::Display for $name {
1189            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1190                write!(
1191                    f,
1192                    "{{\"id\": \"{}\", \"value\": {}}}",
1193                    self.id(),
1194                    self.val()
1195                )
1196            }
1197        }
1198    };
1199}
1200
1201macro_rules! mqtt_property_u32 {
1202    ($name:ident, $id:expr, $validator:expr) => {
1203        mqtt_property_common!($name, $id, [u8; 4]);
1204
1205        impl serde::Serialize for $name {
1206            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1207            where
1208                S: serde::Serializer,
1209            {
1210                let mut s = serializer.serialize_struct(stringify!($name), 2)?;
1211                s.serialize_field("id", &($id as u8))?;
1212                s.serialize_field("value", &self.val())?;
1213                s.end()
1214            }
1215        }
1216
1217        impl $name {
1218            /// Creates a new u32 property with the given value.
1219            ///
1220            /// # Parameters
1221            ///
1222            /// * `v` - The u32 value to set
1223            ///
1224            /// # Returns
1225            ///
1226            /// * `Ok(Self)` - Successfully created property
1227            /// * `Err(MqttError)` - If the value fails validation
1228            ///
1229            /// # Examples
1230            ///
1231            /// ```ignore
1232            /// let prop = MessageExpiryInterval::new(300).unwrap();
1233            /// ```
1234            pub fn new(v: u32) -> Result<Self, MqttError> {
1235                if let Some(validator) = $validator {
1236                    validator(v)?;
1237                }
1238                Ok(Self {
1239                    id_bytes: [$id as u8],
1240                    value: v.to_be_bytes(),
1241                })
1242            }
1243
1244            /// Parses a u32 property from the given byte slice.
1245            ///
1246            /// # Parameters
1247            ///
1248            /// * `bytes` - The byte slice to parse from
1249            ///
1250            /// # Returns
1251            ///
1252            /// * `Ok((Self, usize))` - The parsed property and number of bytes consumed
1253            /// * `Err(MqttError)` - If parsing fails or validation fails
1254            ///
1255            /// # Examples
1256            ///
1257            /// ```ignore
1258            /// let data = &[0x00, 0x00, 0x01, 0x2C]; // 300 in big-endian
1259            /// let (prop, consumed) = MessageExpiryInterval::parse(data).unwrap();
1260            /// assert_eq!(consumed, 4);
1261            /// ```
1262            pub fn parse(bytes: &[u8]) -> Result<(Self, usize), MqttError> {
1263                if bytes.len() < 4 {
1264                    return Err(MqttError::MalformedPacket);
1265                }
1266                let v = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
1267                if let Some(validator) = $validator {
1268                    validator(v)?;
1269                }
1270                Ok((
1271                    Self {
1272                        id_bytes: [$id as u8],
1273                        value: bytes[..4].try_into().unwrap(),
1274                    },
1275                    4,
1276                ))
1277            }
1278
1279            /// Converts the property to I/O slices for efficient transmission.
1280            ///
1281            /// # Returns
1282            ///
1283            /// A vector of I/O slices containing the property data.
1284            ///
1285            /// # Examples
1286            ///
1287            /// ```ignore
1288            /// let prop = MessageExpiryInterval::new(300).unwrap();
1289            /// let buffers = prop.to_buffers();
1290            /// ```
1291            #[cfg(feature = "std")]
1292            pub fn to_buffers(&self) -> Vec<IoSlice<'_>> {
1293                vec![IoSlice::new(&self.id_bytes), IoSlice::new(&self.value)]
1294            }
1295
1296            /// Converts the property to a continuous buffer.
1297            ///
1298            /// # Returns
1299            ///
1300            /// A byte vector containing the complete property data.
1301            ///
1302            /// # Examples
1303            ///
1304            /// ```ignore
1305            /// let prop = Property::new(...).unwrap();
1306            /// let buffer = prop.to_continuous_buffer();
1307            /// ```
1308            pub fn to_continuous_buffer(&self) -> Vec<u8> {
1309                let mut buf = Vec::new();
1310                buf.extend_from_slice(&self.id_bytes);
1311                buf.extend_from_slice(&self.value);
1312                buf
1313            }
1314
1315            /// Returns the u32 value of this property.
1316            ///
1317            /// # Returns
1318            ///
1319            /// The u32 value.
1320            ///
1321            /// # Examples
1322            ///
1323            /// ```ignore
1324            /// let prop = MessageExpiryInterval::new(300).unwrap();
1325            /// assert_eq!(prop.val(), 300);
1326            /// ```
1327            pub fn val(&self) -> u32 {
1328                u32::from_be_bytes([self.value[0], self.value[1], self.value[2], self.value[3]])
1329            }
1330
1331            /// Returns the total size of this property in bytes.
1332            ///
1333            /// This includes the property ID (1 byte) plus the u32 value (4 bytes).
1334            ///
1335            /// # Returns
1336            ///
1337            /// The total size in bytes (always 5 for u32 properties).
1338            ///
1339            /// # Examples
1340            ///
1341            /// ```ignore
1342            /// let prop = MessageExpiryInterval::new(300).unwrap();
1343            /// assert_eq!(prop.size(), 5);
1344            /// ```
1345            pub fn size(&self) -> usize {
1346                1 + self.value.len()
1347            }
1348        }
1349
1350        impl fmt::Display for $name {
1351            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1352                write!(
1353                    f,
1354                    "{{\"id\": \"{}\", \"value\": {}}}",
1355                    self.id(),
1356                    self.val()
1357                )
1358            }
1359        }
1360    };
1361}
1362
1363macro_rules! mqtt_property_variable_integer {
1364    ($name:ident, $id:expr, $validator:expr) => {
1365        mqtt_property_common!($name, $id, VariableByteInteger);
1366
1367        impl serde::Serialize for $name {
1368            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1369            where
1370                S: serde::Serializer,
1371            {
1372                let mut s = serializer.serialize_struct(stringify!($name), 2)?;
1373                s.serialize_field("id", &($id as u8))?;
1374                s.serialize_field("val", &self.val())?;
1375                s.end()
1376            }
1377        }
1378
1379        impl $name {
1380            /// Creates a new variable integer property with the given value.
1381            ///
1382            /// # Parameters
1383            ///
1384            /// * `v` - The u32 value to set (encoded as variable byte integer)
1385            ///
1386            /// # Returns
1387            ///
1388            /// * `Ok(Self)` - Successfully created property
1389            /// * `Err(MqttError)` - If the value fails validation or is out of range
1390            ///
1391            /// # Examples
1392            ///
1393            /// ```ignore
1394            /// let prop = SubscriptionIdentifier::new(42).unwrap();
1395            /// ```
1396            pub fn new(v: u32) -> Result<Self, MqttError> {
1397                let vbi = VariableByteInteger::from_u32(v).ok_or(MqttError::ValueOutOfRange)?;
1398                if let Some(validator) = $validator {
1399                    validator(v)?;
1400                }
1401                Ok(Self {
1402                    id_bytes: [$id as u8],
1403                    value: vbi,
1404                })
1405            }
1406
1407            /// Parses a variable integer property from the given byte slice.
1408            ///
1409            /// # Parameters
1410            ///
1411            /// * `bytes` - The byte slice to parse from
1412            ///
1413            /// # Returns
1414            ///
1415            /// * `Ok((Self, usize))` - The parsed property and number of bytes consumed
1416            /// * `Err(MqttError)` - If parsing fails or validation fails
1417            ///
1418            /// # Examples
1419            ///
1420            /// ```ignore
1421            /// let data = &[0x2A]; // 42 as variable byte integer
1422            /// let (prop, consumed) = SubscriptionIdentifier::parse(data).unwrap();
1423            /// assert_eq!(consumed, 1);
1424            /// ```
1425            pub fn parse(bytes: &[u8]) -> Result<(Self, usize), MqttError> {
1426                match VariableByteInteger::decode_stream(bytes) {
1427                    DecodeResult::Ok(vbi, len) => {
1428                        if let Some(validator) = $validator {
1429                            validator(vbi.to_u32())?;
1430                        }
1431                        Ok((
1432                            Self {
1433                                id_bytes: [$id as u8],
1434                                value: vbi,
1435                            },
1436                            len,
1437                        ))
1438                    }
1439                    DecodeResult::Incomplete => Err(MqttError::InsufficientBytes),
1440                    DecodeResult::Err(_) => Err(MqttError::InsufficientBytes),
1441                }
1442            }
1443
1444            /// Converts the property to I/O slices for efficient transmission.
1445            ///
1446            /// # Returns
1447            ///
1448            /// A vector of I/O slices containing the property data.
1449            ///
1450            /// # Examples
1451            ///
1452            /// ```ignore
1453            /// let prop = SubscriptionIdentifier::new(42).unwrap();
1454            /// let buffers = prop.to_buffers();
1455            /// ```
1456            #[cfg(feature = "std")]
1457            pub fn to_buffers(&self) -> Vec<IoSlice<'_>> {
1458                vec![
1459                    IoSlice::new(&self.id_bytes),
1460                    IoSlice::new(&self.value.as_bytes()),
1461                ]
1462            }
1463
1464            /// Converts the property to a continuous buffer.
1465            ///
1466            /// # Returns
1467            ///
1468            /// A byte vector containing the complete property data.
1469            ///
1470            /// # Examples
1471            ///
1472            /// ```ignore
1473            /// let prop = Property::new(...).unwrap();
1474            /// let buffer = prop.to_continuous_buffer();
1475            /// ```
1476            pub fn to_continuous_buffer(&self) -> Vec<u8> {
1477                let mut buf = Vec::new();
1478                buf.extend_from_slice(&self.id_bytes);
1479                buf.append(&mut self.value.to_continuous_buffer());
1480                buf
1481            }
1482
1483            /// Returns the u32 value of this property.
1484            ///
1485            /// # Returns
1486            ///
1487            /// The u32 value encoded as a variable byte integer.
1488            ///
1489            /// # Examples
1490            ///
1491            /// ```ignore
1492            /// let prop = SubscriptionIdentifier::new(42).unwrap();
1493            /// assert_eq!(prop.val(), 42);
1494            /// ```
1495            pub fn val(&self) -> u32 {
1496                self.value.to_u32()
1497            }
1498
1499            /// Returns the total size of this property in bytes.
1500            ///
1501            /// This includes the property ID (1 byte) plus the variable byte integer size.
1502            ///
1503            /// # Returns
1504            ///
1505            /// The total size in bytes.
1506            ///
1507            /// # Examples
1508            ///
1509            /// ```ignore
1510            /// let prop = SubscriptionIdentifier::new(42).unwrap();
1511            /// assert_eq!(prop.size(), 2); // 1 (ID) + 1 (value < 128)
1512            /// ```
1513            pub fn size(&self) -> usize {
1514                1 + self.value.size()
1515            }
1516        }
1517
1518        impl fmt::Display for $name {
1519            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1520                write!(
1521                    f,
1522                    "{{\"id\": \"{}\", \"value\": {}}}",
1523                    self.id(),
1524                    self.val()
1525                )
1526            }
1527        }
1528    };
1529}
1530
1531type U16Validator = fn(u16) -> Result<(), MqttError>;
1532type U32Validator = fn(u32) -> Result<(), MqttError>;
1533
1534mqtt_property_u8_custom_new!(
1535    PayloadFormatIndicator,
1536    PropertyId::PayloadFormatIndicator,
1537    Some(|v| {
1538        if v > 1 {
1539            Err(MqttError::ProtocolError)
1540        } else {
1541            Ok(())
1542        }
1543    })
1544);
1545impl PayloadFormatIndicator {
1546    /// Creates a new PayloadFormatIndicator property.
1547    ///
1548    /// # Parameters
1549    ///
1550    /// * `v` - The PayloadFormat enum value
1551    ///
1552    /// # Returns
1553    ///
1554    /// * `Ok(Self)` - Successfully created property
1555    /// * `Err(MqttError)` - If creation fails
1556    ///
1557    /// # Examples
1558    ///
1559    /// ```ignore
1560    /// let prop = PayloadFormatIndicator::new(PayloadFormat::Utf8String).unwrap();
1561    /// ```
1562    pub fn new(v: PayloadFormat) -> Result<Self, MqttError> {
1563        Ok(Self {
1564            id_bytes: [PropertyId::PayloadFormatIndicator.as_u8(); 1],
1565            value: [v as u8],
1566        })
1567    }
1568}
1569
1570mqtt_property_u32!(
1571    MessageExpiryInterval,
1572    PropertyId::MessageExpiryInterval,
1573    None::<U32Validator>
1574);
1575mqtt_property_string!(ContentType, PropertyId::ContentType);
1576mqtt_property_string!(ResponseTopic, PropertyId::ResponseTopic);
1577mqtt_property_binary!(CorrelationData, PropertyId::CorrelationData);
1578mqtt_property_variable_integer!(
1579    SubscriptionIdentifier,
1580    PropertyId::SubscriptionIdentifier,
1581    Some(|v| {
1582        if v == 0 {
1583            Err(MqttError::ProtocolError)
1584        } else {
1585            Ok(())
1586        }
1587    })
1588);
1589mqtt_property_u32!(
1590    SessionExpiryInterval,
1591    PropertyId::SessionExpiryInterval,
1592    None::<U32Validator>
1593);
1594mqtt_property_string!(
1595    AssignedClientIdentifier,
1596    PropertyId::AssignedClientIdentifier
1597);
1598mqtt_property_u16!(
1599    ServerKeepAlive,
1600    PropertyId::ServerKeepAlive,
1601    None::<U16Validator>
1602);
1603mqtt_property_string!(AuthenticationMethod, PropertyId::AuthenticationMethod);
1604mqtt_property_binary!(AuthenticationData, PropertyId::AuthenticationData);
1605mqtt_property_u8!(
1606    RequestProblemInformation,
1607    PropertyId::RequestProblemInformation,
1608    Some(|v| {
1609        if v > 1 {
1610            Err(MqttError::ProtocolError)
1611        } else {
1612            Ok(())
1613        }
1614    })
1615);
1616mqtt_property_u32!(
1617    WillDelayInterval,
1618    PropertyId::WillDelayInterval,
1619    None::<U32Validator>
1620);
1621mqtt_property_u8!(
1622    RequestResponseInformation,
1623    PropertyId::RequestResponseInformation,
1624    Some(|v| {
1625        if v > 1 {
1626            Err(MqttError::ProtocolError)
1627        } else {
1628            Ok(())
1629        }
1630    })
1631);
1632mqtt_property_string!(ResponseInformation, PropertyId::ResponseInformation);
1633mqtt_property_string!(ServerReference, PropertyId::ServerReference);
1634mqtt_property_string!(ReasonString, PropertyId::ReasonString);
1635mqtt_property_u16!(
1636    ReceiveMaximum,
1637    PropertyId::ReceiveMaximum,
1638    Some(|v| {
1639        if v == 0 {
1640            Err(MqttError::ProtocolError)
1641        } else {
1642            Ok(())
1643        }
1644    })
1645);
1646mqtt_property_u16!(
1647    TopicAliasMaximum,
1648    PropertyId::TopicAliasMaximum,
1649    None::<U16Validator>
1650);
1651mqtt_property_u16!(
1652    TopicAlias,
1653    PropertyId::TopicAlias,
1654    Some(|v| {
1655        if v == 0 {
1656            Err(MqttError::ProtocolError)
1657        } else {
1658            Ok(())
1659        }
1660    })
1661);
1662mqtt_property_u8!(
1663    MaximumQos,
1664    PropertyId::MaximumQos,
1665    Some(|v| {
1666        if v > 1 {
1667            Err(MqttError::ProtocolError)
1668        } else {
1669            Ok(())
1670        }
1671    })
1672);
1673mqtt_property_u8!(
1674    RetainAvailable,
1675    PropertyId::RetainAvailable,
1676    Some(|v| {
1677        if v > 1 {
1678            Err(MqttError::ProtocolError)
1679        } else {
1680            Ok(())
1681        }
1682    })
1683);
1684mqtt_property_string_pair!(UserProperty, PropertyId::UserProperty);
1685mqtt_property_u32!(
1686    MaximumPacketSize,
1687    PropertyId::MaximumPacketSize,
1688    Some(|v| {
1689        if v == 0 {
1690            Err(MqttError::ProtocolError)
1691        } else {
1692            Ok(())
1693        }
1694    })
1695);
1696mqtt_property_u8!(
1697    WildcardSubscriptionAvailable,
1698    PropertyId::WildcardSubscriptionAvailable,
1699    Some(|v| {
1700        if v > 1 {
1701            Err(MqttError::ProtocolError)
1702        } else {
1703            Ok(())
1704        }
1705    })
1706);
1707mqtt_property_u8!(
1708    SubscriptionIdentifierAvailable,
1709    PropertyId::SubscriptionIdentifierAvailable,
1710    Some(|v| {
1711        if v > 1 {
1712            Err(MqttError::ProtocolError)
1713        } else {
1714            Ok(())
1715        }
1716    })
1717);
1718mqtt_property_u8!(
1719    SharedSubscriptionAvailable,
1720    PropertyId::SharedSubscriptionAvailable,
1721    Some(|v| {
1722        if v > 1 {
1723            Err(MqttError::ProtocolError)
1724        } else {
1725            Ok(())
1726        }
1727    })
1728);
1729
1730/// MQTT v5.0 Property enum
1731///
1732/// This enum represents all possible MQTT v5.0 properties that can be included
1733/// in various packet types. Each variant wraps a specific property type with
1734/// its associated data and validation rules.
1735///
1736/// Properties provide extensibility to MQTT packets, allowing clients and servers
1737/// to communicate additional metadata, control flow information, and authentication data.
1738///
1739/// # Usage
1740///
1741/// Properties are typically collected in a `Vec<Property>` and included in
1742/// MQTT packets during construction or parsing.
1743///
1744/// # Examples
1745///
1746/// ```ignore
1747/// use mqtt_protocol_core::mqtt;
1748///
1749/// // Create a message expiry property
1750/// let expiry = mqtt::packet::MessageExpiryInterval::new(3600).unwrap();
1751/// let property = mqtt::packet::Property::MessageExpiryInterval(expiry);
1752///
1753/// // Create user property
1754/// let user_prop = mqtt::packet::UserProperty::new("key", "value").unwrap();
1755/// let property = mqtt::packet::Property::UserProperty(user_prop);
1756/// ```
1757#[derive(Debug, Serialize, PartialEq, Eq, Clone)]
1758#[allow(clippy::large_enum_variant)]
1759pub enum Property {
1760    PayloadFormatIndicator(PayloadFormatIndicator),
1761    MessageExpiryInterval(MessageExpiryInterval),
1762    ContentType(ContentType),
1763    ResponseTopic(ResponseTopic),
1764    CorrelationData(CorrelationData),
1765    SubscriptionIdentifier(SubscriptionIdentifier),
1766    SessionExpiryInterval(SessionExpiryInterval),
1767    AssignedClientIdentifier(AssignedClientIdentifier),
1768    ServerKeepAlive(ServerKeepAlive),
1769    AuthenticationMethod(AuthenticationMethod),
1770    AuthenticationData(AuthenticationData),
1771    RequestProblemInformation(RequestProblemInformation),
1772    WillDelayInterval(WillDelayInterval),
1773    RequestResponseInformation(RequestResponseInformation),
1774    ResponseInformation(ResponseInformation),
1775    ServerReference(ServerReference),
1776    ReasonString(ReasonString),
1777    ReceiveMaximum(ReceiveMaximum),
1778    TopicAliasMaximum(TopicAliasMaximum),
1779    TopicAlias(TopicAlias),
1780    MaximumQos(MaximumQos),
1781    RetainAvailable(RetainAvailable),
1782    UserProperty(UserProperty),
1783    MaximumPacketSize(MaximumPacketSize),
1784    WildcardSubscriptionAvailable(WildcardSubscriptionAvailable),
1785    SubscriptionIdentifierAvailable(SubscriptionIdentifierAvailable),
1786    SharedSubscriptionAvailable(SharedSubscriptionAvailable),
1787}
1788
1789impl fmt::Display for Property {
1790    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1791        match self {
1792            Property::PayloadFormatIndicator(p) => write!(f, "{p}"),
1793            Property::MessageExpiryInterval(p) => write!(f, "{p}"),
1794            Property::ContentType(p) => write!(f, "{p}"),
1795            Property::ResponseTopic(p) => write!(f, "{p}"),
1796            Property::CorrelationData(p) => write!(f, "{p}"),
1797            Property::SubscriptionIdentifier(p) => write!(f, "{p}"),
1798            Property::SessionExpiryInterval(p) => write!(f, "{p}"),
1799            Property::AssignedClientIdentifier(p) => write!(f, "{p}"),
1800            Property::ServerKeepAlive(p) => write!(f, "{p}"),
1801            Property::AuthenticationMethod(p) => write!(f, "{p}"),
1802            Property::AuthenticationData(p) => write!(f, "{p}"),
1803            Property::RequestProblemInformation(p) => write!(f, "{p}"),
1804            Property::WillDelayInterval(p) => write!(f, "{p}"),
1805            Property::RequestResponseInformation(p) => write!(f, "{p}"),
1806            Property::ResponseInformation(p) => write!(f, "{p}"),
1807            Property::ServerReference(p) => write!(f, "{p}"),
1808            Property::ReasonString(p) => write!(f, "{p}"),
1809            Property::ReceiveMaximum(p) => write!(f, "{p}"),
1810            Property::TopicAliasMaximum(p) => write!(f, "{p}"),
1811            Property::TopicAlias(p) => write!(f, "{p}"),
1812            Property::MaximumQos(p) => write!(f, "{p}"),
1813            Property::RetainAvailable(p) => write!(f, "{p}"),
1814            Property::UserProperty(p) => write!(f, "{p}"),
1815            Property::MaximumPacketSize(p) => write!(f, "{p}"),
1816            Property::WildcardSubscriptionAvailable(p) => write!(f, "{p}"),
1817            Property::SubscriptionIdentifierAvailable(p) => write!(f, "{p}"),
1818            Property::SharedSubscriptionAvailable(p) => write!(f, "{p}"),
1819        }
1820    }
1821}
1822
1823/// Trait for accessing property values in a type-safe manner
1824///
1825/// This trait provides methods to extract values from `Property` enum variants
1826/// without having to match on each variant explicitly. Methods return `Option`
1827/// to handle cases where the property type doesn't match the requested type.
1828///
1829/// # Examples
1830///
1831/// ```ignore
1832/// use mqtt_protocol_core::mqtt;
1833///
1834/// let prop = mqtt::packet::Property::MessageExpiryInterval(
1835///     mqtt::packet::MessageExpiryInterval::new(3600).unwrap()
1836/// );
1837///
1838/// // Extract the u32 value
1839/// if let Some(interval) = prop.as_u32() {
1840///     println!("Message expires in {} seconds", interval);
1841/// }
1842/// ```
1843pub trait PropertyValueAccess {
1844    /// Extract u8 value from byte-based properties
1845    ///
1846    /// Returns `Some(u8)` for properties that store single-byte values,
1847    /// `None` for other property types.
1848    fn as_u8(&self) -> Option<u8>;
1849
1850    /// Extract u16 value from two-byte properties
1851    ///
1852    /// Returns `Some(u16)` for properties that store two-byte values,
1853    /// `None` for other property types.
1854    fn as_u16(&self) -> Option<u16>;
1855
1856    /// Extract u32 value from four-byte properties
1857    ///
1858    /// Returns `Some(u32)` for properties that store four-byte values,
1859    /// `None` for other property types.
1860    fn as_u32(&self) -> Option<u32>;
1861
1862    /// Extract string value from string-based properties
1863    ///
1864    /// Returns `Some(&str)` for properties that store UTF-8 strings,
1865    /// `None` for other property types.
1866    fn as_str(&self) -> Option<&str>;
1867
1868    /// Extract binary data from binary-based properties
1869    ///
1870    /// Returns `Some(&[u8])` for properties that store binary data,
1871    /// `None` for other property types.
1872    fn as_bytes(&self) -> Option<&[u8]>;
1873
1874    /// Extract key-value pair from UserProperty
1875    ///
1876    /// Returns `Some((key, value))` for UserProperty, `None` for other property types.
1877    fn as_key_value(&self) -> Option<(&str, &str)>;
1878}
1879
1880impl PropertyValueAccess for Property {
1881    fn as_u8(&self) -> Option<u8> {
1882        match self {
1883            // All property types that return u8
1884            Property::PayloadFormatIndicator(p) => Some(p.val()),
1885            Property::MaximumQos(p) => Some(p.val()),
1886            Property::RetainAvailable(p) => Some(p.val()),
1887            Property::RequestProblemInformation(p) => Some(p.val()),
1888            Property::RequestResponseInformation(p) => Some(p.val()),
1889            Property::WildcardSubscriptionAvailable(p) => Some(p.val()),
1890            Property::SubscriptionIdentifierAvailable(p) => Some(p.val()),
1891            Property::SharedSubscriptionAvailable(p) => Some(p.val()),
1892            _ => None,
1893        }
1894    }
1895
1896    fn as_u16(&self) -> Option<u16> {
1897        match self {
1898            // All property types that return u16
1899            Property::TopicAlias(p) => Some(p.val()),
1900            Property::ReceiveMaximum(p) => Some(p.val()),
1901            Property::TopicAliasMaximum(p) => Some(p.val()),
1902            Property::ServerKeepAlive(p) => Some(p.val()),
1903            _ => None,
1904        }
1905    }
1906
1907    fn as_u32(&self) -> Option<u32> {
1908        match self {
1909            // All property types that return u32
1910            Property::MessageExpiryInterval(p) => Some(p.val()),
1911            Property::SessionExpiryInterval(p) => Some(p.val()),
1912            Property::WillDelayInterval(p) => Some(p.val()),
1913            Property::MaximumPacketSize(p) => Some(p.val()),
1914            Property::SubscriptionIdentifier(p) => Some(p.val()),
1915            _ => None,
1916        }
1917    }
1918
1919    fn as_str(&self) -> Option<&str> {
1920        match self {
1921            // All property types that return strings
1922            Property::ContentType(p) => Some(p.val()),
1923            Property::ResponseTopic(p) => Some(p.val()),
1924            Property::AssignedClientIdentifier(p) => Some(p.val()),
1925            Property::AuthenticationMethod(p) => Some(p.val()),
1926            Property::ResponseInformation(p) => Some(p.val()),
1927            Property::ServerReference(p) => Some(p.val()),
1928            Property::ReasonString(p) => Some(p.val()),
1929            _ => None,
1930        }
1931    }
1932
1933    fn as_bytes(&self) -> Option<&[u8]> {
1934        match self {
1935            // Property types that return binary data
1936            Property::CorrelationData(p) => Some(p.val()),
1937            Property::AuthenticationData(p) => Some(p.val()),
1938            _ => None,
1939        }
1940    }
1941
1942    fn as_key_value(&self) -> Option<(&str, &str)> {
1943        match self {
1944            // Property types that return key-value pairs
1945            Property::UserProperty(p) => Some((p.key(), p.val())),
1946            _ => None,
1947        }
1948    }
1949}
1950
1951impl Property {
1952    /// Get the property identifier for this property
1953    ///
1954    /// Returns the `PropertyId` that corresponds to this property type.
1955    /// This is useful for determining the property type without matching
1956    /// on the enum variant.
1957    ///
1958    /// # Examples
1959    ///
1960    /// ```ignore
1961    /// use mqtt_protocol_core::mqtt;
1962    ///
1963    /// let prop = mqtt::packet::Property::MessageExpiryInterval(
1964    ///     mqtt::packet::MessageExpiryInterval::new(3600).unwrap()
1965    /// );
1966    /// assert_eq!(prop.id(), mqtt::packet::PropertyId::MessageExpiryInterval);
1967    /// ```
1968    pub fn id(&self) -> PropertyId {
1969        match self {
1970            Property::PayloadFormatIndicator(p) => p.id(),
1971            Property::MessageExpiryInterval(p) => p.id(),
1972            Property::ContentType(p) => p.id(),
1973            Property::ResponseTopic(p) => p.id(),
1974            Property::CorrelationData(p) => p.id(),
1975            Property::SubscriptionIdentifier(p) => p.id(),
1976            Property::SessionExpiryInterval(p) => p.id(),
1977            Property::AssignedClientIdentifier(p) => p.id(),
1978            Property::ServerKeepAlive(p) => p.id(),
1979            Property::AuthenticationMethod(p) => p.id(),
1980            Property::AuthenticationData(p) => p.id(),
1981            Property::RequestProblemInformation(p) => p.id(),
1982            Property::WillDelayInterval(p) => p.id(),
1983            Property::RequestResponseInformation(p) => p.id(),
1984            Property::ResponseInformation(p) => p.id(),
1985            Property::ServerReference(p) => p.id(),
1986            Property::ReasonString(p) => p.id(),
1987            Property::ReceiveMaximum(p) => p.id(),
1988            Property::TopicAliasMaximum(p) => p.id(),
1989            Property::TopicAlias(p) => p.id(),
1990            Property::MaximumQos(p) => p.id(),
1991            Property::RetainAvailable(p) => p.id(),
1992            Property::UserProperty(p) => p.id(),
1993            Property::MaximumPacketSize(p) => p.id(),
1994            Property::WildcardSubscriptionAvailable(p) => p.id(),
1995            Property::SubscriptionIdentifierAvailable(p) => p.id(),
1996            Property::SharedSubscriptionAvailable(p) => p.id(),
1997        }
1998    }
1999
2000    /// Get the encoded size of this property in bytes
2001    ///
2002    /// Returns the total number of bytes required to encode this property
2003    /// in the MQTT wire format, including the property identifier and
2004    /// any length prefixes.
2005    ///
2006    /// # Examples
2007    ///
2008    /// ```ignore
2009    /// use mqtt_protocol_core::mqtt;
2010    ///
2011    /// let prop = mqtt::packet::Property::MessageExpiryInterval(
2012    ///     mqtt::packet::MessageExpiryInterval::new(3600).unwrap()
2013    /// );
2014    /// let size = prop.size(); // 1 byte ID + 4 bytes value = 5 bytes
2015    /// ```
2016    pub fn size(&self) -> usize {
2017        match self {
2018            Property::PayloadFormatIndicator(p) => p.size(),
2019            Property::MessageExpiryInterval(p) => p.size(),
2020            Property::ContentType(p) => p.size(),
2021            Property::ResponseTopic(p) => p.size(),
2022            Property::CorrelationData(p) => p.size(),
2023            Property::SubscriptionIdentifier(p) => p.size(),
2024            Property::SessionExpiryInterval(p) => p.size(),
2025            Property::AssignedClientIdentifier(p) => p.size(),
2026            Property::ServerKeepAlive(p) => p.size(),
2027            Property::AuthenticationMethod(p) => p.size(),
2028            Property::AuthenticationData(p) => p.size(),
2029            Property::RequestProblemInformation(p) => p.size(),
2030            Property::WillDelayInterval(p) => p.size(),
2031            Property::RequestResponseInformation(p) => p.size(),
2032            Property::ResponseInformation(p) => p.size(),
2033            Property::ServerReference(p) => p.size(),
2034            Property::ReasonString(p) => p.size(),
2035            Property::ReceiveMaximum(p) => p.size(),
2036            Property::TopicAliasMaximum(p) => p.size(),
2037            Property::TopicAlias(p) => p.size(),
2038            Property::MaximumQos(p) => p.size(),
2039            Property::RetainAvailable(p) => p.size(),
2040            Property::UserProperty(p) => p.size(),
2041            Property::MaximumPacketSize(p) => p.size(),
2042            Property::WildcardSubscriptionAvailable(p) => p.size(),
2043            Property::SubscriptionIdentifierAvailable(p) => p.size(),
2044            Property::SharedSubscriptionAvailable(p) => p.size(),
2045        }
2046    }
2047
2048    /// Create IoSlice buffers for efficient network I/O
2049    ///
2050    /// Returns a vector of `IoSlice` objects that can be used for vectored I/O
2051    /// operations, allowing zero-copy writes to network sockets. The buffers
2052    /// include the property identifier and the encoded property value.
2053    ///
2054    /// # Examples
2055    ///
2056    /// ```ignore
2057    /// use mqtt_protocol_core::mqtt;
2058    ///
2059    /// let prop = mqtt::packet::Property::ContentType(
2060    ///     mqtt::packet::ContentType::new("application/json").unwrap()
2061    /// );
2062    /// let buffers = prop.to_buffers();
2063    /// // Can be used with vectored write operations
2064    /// // socket.write_vectored(&buffers)?;
2065    /// ```
2066    #[cfg(feature = "std")]
2067    pub fn to_buffers(&self) -> Vec<IoSlice<'_>> {
2068        match self {
2069            Property::PayloadFormatIndicator(p) => p.to_buffers(),
2070            Property::MessageExpiryInterval(p) => p.to_buffers(),
2071            Property::ContentType(p) => p.to_buffers(),
2072            Property::ResponseTopic(p) => p.to_buffers(),
2073            Property::CorrelationData(p) => p.to_buffers(),
2074            Property::SubscriptionIdentifier(p) => p.to_buffers(),
2075            Property::SessionExpiryInterval(p) => p.to_buffers(),
2076            Property::AssignedClientIdentifier(p) => p.to_buffers(),
2077            Property::ServerKeepAlive(p) => p.to_buffers(),
2078            Property::AuthenticationMethod(p) => p.to_buffers(),
2079            Property::AuthenticationData(p) => p.to_buffers(),
2080            Property::RequestProblemInformation(p) => p.to_buffers(),
2081            Property::WillDelayInterval(p) => p.to_buffers(),
2082            Property::RequestResponseInformation(p) => p.to_buffers(),
2083            Property::ResponseInformation(p) => p.to_buffers(),
2084            Property::ServerReference(p) => p.to_buffers(),
2085            Property::ReasonString(p) => p.to_buffers(),
2086            Property::ReceiveMaximum(p) => p.to_buffers(),
2087            Property::TopicAliasMaximum(p) => p.to_buffers(),
2088            Property::TopicAlias(p) => p.to_buffers(),
2089            Property::MaximumQos(p) => p.to_buffers(),
2090            Property::RetainAvailable(p) => p.to_buffers(),
2091            Property::UserProperty(p) => p.to_buffers(),
2092            Property::MaximumPacketSize(p) => p.to_buffers(),
2093            Property::WildcardSubscriptionAvailable(p) => p.to_buffers(),
2094            Property::SubscriptionIdentifierAvailable(p) => p.to_buffers(),
2095            Property::SharedSubscriptionAvailable(p) => p.to_buffers(),
2096        }
2097    }
2098
2099    /// Create a continuous buffer containing the complete property data
2100    ///
2101    /// Returns a vector containing all property bytes in a single continuous buffer.
2102    /// This method is compatible with no-std environments and provides an alternative
2103    /// to [`to_buffers()`] when vectored I/O is not needed.
2104    ///
2105    /// The returned buffer includes the property identifier and the encoded property value.
2106    ///
2107    /// # Examples
2108    ///
2109    /// ```ignore
2110    /// use mqtt_protocol_core::mqtt;
2111    ///
2112    /// let prop = mqtt::packet::Property::ContentType(
2113    ///     mqtt::packet::ContentType::new("application/json").unwrap()
2114    /// );
2115    /// let buffer = prop.to_continuous_buffer();
2116    /// // buffer contains all property bytes
2117    /// ```
2118    ///
2119    /// [`to_buffers()`]: #method.to_buffers
2120    pub fn to_continuous_buffer(&self) -> Vec<u8> {
2121        match self {
2122            Property::PayloadFormatIndicator(p) => p.to_continuous_buffer(),
2123            Property::MessageExpiryInterval(p) => p.to_continuous_buffer(),
2124            Property::ContentType(p) => p.to_continuous_buffer(),
2125            Property::ResponseTopic(p) => p.to_continuous_buffer(),
2126            Property::CorrelationData(p) => p.to_continuous_buffer(),
2127            Property::SubscriptionIdentifier(p) => p.to_continuous_buffer(),
2128            Property::SessionExpiryInterval(p) => p.to_continuous_buffer(),
2129            Property::AssignedClientIdentifier(p) => p.to_continuous_buffer(),
2130            Property::ServerKeepAlive(p) => p.to_continuous_buffer(),
2131            Property::AuthenticationMethod(p) => p.to_continuous_buffer(),
2132            Property::AuthenticationData(p) => p.to_continuous_buffer(),
2133            Property::RequestProblemInformation(p) => p.to_continuous_buffer(),
2134            Property::WillDelayInterval(p) => p.to_continuous_buffer(),
2135            Property::RequestResponseInformation(p) => p.to_continuous_buffer(),
2136            Property::ResponseInformation(p) => p.to_continuous_buffer(),
2137            Property::ServerReference(p) => p.to_continuous_buffer(),
2138            Property::ReasonString(p) => p.to_continuous_buffer(),
2139            Property::ReceiveMaximum(p) => p.to_continuous_buffer(),
2140            Property::TopicAliasMaximum(p) => p.to_continuous_buffer(),
2141            Property::TopicAlias(p) => p.to_continuous_buffer(),
2142            Property::MaximumQos(p) => p.to_continuous_buffer(),
2143            Property::RetainAvailable(p) => p.to_continuous_buffer(),
2144            Property::UserProperty(p) => p.to_continuous_buffer(),
2145            Property::MaximumPacketSize(p) => p.to_continuous_buffer(),
2146            Property::WildcardSubscriptionAvailable(p) => p.to_continuous_buffer(),
2147            Property::SubscriptionIdentifierAvailable(p) => p.to_continuous_buffer(),
2148            Property::SharedSubscriptionAvailable(p) => p.to_continuous_buffer(),
2149        }
2150    }
2151
2152    /// Parse a property from a byte sequence
2153    ///
2154    /// Decodes a single MQTT property from a byte buffer according to the MQTT v5.0
2155    /// specification. The buffer must start with a property identifier byte followed
2156    /// by the property value in the appropriate format.
2157    ///
2158    /// # Parameters
2159    ///
2160    /// * `bytes` - Byte buffer containing the encoded property data
2161    ///
2162    /// # Returns
2163    ///
2164    /// * `Ok((Property, bytes_consumed))` - Successfully parsed property and number of bytes consumed
2165    /// * `Err(MqttError::MalformedPacket)` - If the buffer is too short, contains an invalid property ID, or malformed property data
2166    ///
2167    /// # Examples
2168    ///
2169    /// ```ignore
2170    /// use mqtt_protocol_core::mqtt;
2171    ///
2172    /// // Buffer: [property_id, property_data...]
2173    /// let buffer = &[0x02, 0x00, 0x00, 0x0E, 0x10]; // MessageExpiryInterval = 3600
2174    /// let (property, consumed) = mqtt::packet::Property::parse(buffer).unwrap();
2175    ///
2176    /// match property {
2177    ///     mqtt::packet::Property::MessageExpiryInterval(prop) => {
2178    ///         assert_eq!(prop.val(), 3600);
2179    ///     }
2180    ///     _ => panic!("Wrong property type"),
2181    /// }
2182    /// assert_eq!(consumed, 5);
2183    /// ```
2184    pub fn parse(bytes: &[u8]) -> Result<(Self, usize), MqttError> {
2185        if bytes.is_empty() {
2186            return Err(MqttError::MalformedPacket);
2187        }
2188
2189        let id = PropertyId::try_from(bytes[0]).map_err(|_| MqttError::MalformedPacket)?;
2190
2191        let (prop, len) = match id {
2192            PropertyId::PayloadFormatIndicator => {
2193                let (p, l) = PayloadFormatIndicator::parse(&bytes[1..])?;
2194                (Self::PayloadFormatIndicator(p), l + 1)
2195            }
2196            PropertyId::MessageExpiryInterval => {
2197                let (p, l) = MessageExpiryInterval::parse(&bytes[1..])?;
2198                (Self::MessageExpiryInterval(p), l + 1)
2199            }
2200            PropertyId::ContentType => {
2201                let (p, l) = ContentType::parse(&bytes[1..])?;
2202                (Self::ContentType(p), l + 1)
2203            }
2204            PropertyId::ResponseTopic => {
2205                let (p, l) = ResponseTopic::parse(&bytes[1..])?;
2206                (Self::ResponseTopic(p), l + 1)
2207            }
2208            PropertyId::CorrelationData => {
2209                let (p, l) = CorrelationData::parse(&bytes[1..])?;
2210                (Self::CorrelationData(p), l + 1)
2211            }
2212            PropertyId::SubscriptionIdentifier => {
2213                let (p, l) = SubscriptionIdentifier::parse(&bytes[1..])?;
2214                (Self::SubscriptionIdentifier(p), l + 1)
2215            }
2216            PropertyId::SessionExpiryInterval => {
2217                let (p, l) = SessionExpiryInterval::parse(&bytes[1..])?;
2218                (Self::SessionExpiryInterval(p), l + 1)
2219            }
2220            PropertyId::AssignedClientIdentifier => {
2221                let (p, l) = AssignedClientIdentifier::parse(&bytes[1..])?;
2222                (Self::AssignedClientIdentifier(p), l + 1)
2223            }
2224            PropertyId::ServerKeepAlive => {
2225                let (p, l) = ServerKeepAlive::parse(&bytes[1..])?;
2226                (Self::ServerKeepAlive(p), l + 1)
2227            }
2228            PropertyId::AuthenticationMethod => {
2229                let (p, l) = AuthenticationMethod::parse(&bytes[1..])?;
2230                (Self::AuthenticationMethod(p), l + 1)
2231            }
2232            PropertyId::AuthenticationData => {
2233                let (p, l) = AuthenticationData::parse(&bytes[1..])?;
2234                (Self::AuthenticationData(p), l + 1)
2235            }
2236            PropertyId::RequestProblemInformation => {
2237                let (p, l) = RequestProblemInformation::parse(&bytes[1..])?;
2238                (Self::RequestProblemInformation(p), l + 1)
2239            }
2240            PropertyId::WillDelayInterval => {
2241                let (p, l) = WillDelayInterval::parse(&bytes[1..])?;
2242                (Self::WillDelayInterval(p), l + 1)
2243            }
2244            PropertyId::RequestResponseInformation => {
2245                let (p, l) = RequestResponseInformation::parse(&bytes[1..])?;
2246                (Self::RequestResponseInformation(p), l + 1)
2247            }
2248            PropertyId::ResponseInformation => {
2249                let (p, l) = ResponseInformation::parse(&bytes[1..])?;
2250                (Self::ResponseInformation(p), l + 1)
2251            }
2252            PropertyId::ServerReference => {
2253                let (p, l) = ServerReference::parse(&bytes[1..])?;
2254                (Self::ServerReference(p), l + 1)
2255            }
2256            PropertyId::ReasonString => {
2257                let (p, l) = ReasonString::parse(&bytes[1..])?;
2258                (Self::ReasonString(p), l + 1)
2259            }
2260            PropertyId::ReceiveMaximum => {
2261                let (p, l) = ReceiveMaximum::parse(&bytes[1..])?;
2262                (Self::ReceiveMaximum(p), l + 1)
2263            }
2264            PropertyId::TopicAliasMaximum => {
2265                let (p, l) = TopicAliasMaximum::parse(&bytes[1..])?;
2266                (Self::TopicAliasMaximum(p), l + 1)
2267            }
2268            PropertyId::TopicAlias => {
2269                let (p, l) = TopicAlias::parse(&bytes[1..])?;
2270                (Self::TopicAlias(p), l + 1)
2271            }
2272            PropertyId::MaximumQos => {
2273                let (p, l) = MaximumQos::parse(&bytes[1..])?;
2274                (Self::MaximumQos(p), l + 1)
2275            }
2276            PropertyId::RetainAvailable => {
2277                let (p, l) = RetainAvailable::parse(&bytes[1..])?;
2278                (Self::RetainAvailable(p), l + 1)
2279            }
2280            PropertyId::UserProperty => {
2281                let (p, l) = UserProperty::parse(&bytes[1..])?;
2282                (Self::UserProperty(p), l + 1)
2283            }
2284            PropertyId::MaximumPacketSize => {
2285                let (p, l) = MaximumPacketSize::parse(&bytes[1..])?;
2286                (Self::MaximumPacketSize(p), l + 1)
2287            }
2288            PropertyId::WildcardSubscriptionAvailable => {
2289                let (p, l) = WildcardSubscriptionAvailable::parse(&bytes[1..])?;
2290                (Self::WildcardSubscriptionAvailable(p), l + 1)
2291            }
2292            PropertyId::SubscriptionIdentifierAvailable => {
2293                let (p, l) = SubscriptionIdentifierAvailable::parse(&bytes[1..])?;
2294                (Self::SubscriptionIdentifierAvailable(p), l + 1)
2295            }
2296            PropertyId::SharedSubscriptionAvailable => {
2297                let (p, l) = SharedSubscriptionAvailable::parse(&bytes[1..])?;
2298                (Self::SharedSubscriptionAvailable(p), l + 1)
2299            }
2300        };
2301
2302        Ok((prop, len))
2303    }
2304}
2305
2306/// Collection of MQTT properties
2307///
2308/// This type alias represents a collection of MQTT v5.0 properties that can be
2309/// included in various packet types. Properties are stored as a vector to preserve
2310/// order and allow multiple instances of certain property types (like UserProperty).
2311///
2312/// # Examples
2313///
2314/// ```ignore
2315/// use mqtt_protocol_core::mqtt;
2316///
2317/// let mut properties = mqtt::packet::Properties::new();
2318///
2319/// // Add a message expiry interval
2320/// let expiry = mqtt::packet::MessageExpiryInterval::new(3600).unwrap();
2321/// properties.push(mqtt::packet::Property::MessageExpiryInterval(expiry));
2322///
2323/// // Add user-defined properties
2324/// let user_prop = mqtt::packet::UserProperty::new("app", "myapp").unwrap();
2325/// properties.push(mqtt::packet::Property::UserProperty(user_prop));
2326/// ```
2327pub type Properties = Vec<Property>;
2328
2329/// Trait for converting properties collection to continuous buffer
2330///
2331/// This trait provides functionality to convert a collection of properties
2332/// into a single continuous buffer compatible with no-std environments.
2333pub trait PropertiesToContinuousBuffer {
2334    /// Convert properties to continuous buffer
2335    ///
2336    /// Returns a vector containing all property bytes in a single continuous buffer.
2337    fn to_continuous_buffer(&self) -> Vec<u8>;
2338}
2339
2340/// Trait for converting properties collection to I/O buffers
2341///
2342/// This trait provides functionality to convert a collection of properties
2343/// into IoSlice buffers suitable for efficient network I/O operations.
2344#[cfg(feature = "std")]
2345pub trait PropertiesToBuffers {
2346    /// Convert properties to IoSlice buffers for vectored I/O
2347    ///
2348    /// Returns a vector of IoSlice objects that can be used with
2349    /// vectored write operations for zero-copy network transmission.
2350    fn to_buffers(&self) -> Vec<IoSlice<'_>>;
2351}
2352
2353/// Implementation of PropertiesToContinuousBuffer for Properties
2354///
2355/// Concatenates continuous buffers from all properties in the collection.
2356impl PropertiesToContinuousBuffer for Properties {
2357    fn to_continuous_buffer(&self) -> Vec<u8> {
2358        let mut result = Vec::new();
2359
2360        for prop in self {
2361            result.append(&mut prop.to_continuous_buffer());
2362        }
2363
2364        result
2365    }
2366}
2367
2368/// Implementation of PropertiesToBuffers for Properties
2369///
2370/// Concatenates IoSlice buffers from all properties in the collection.
2371#[cfg(feature = "std")]
2372impl PropertiesToBuffers for Properties {
2373    fn to_buffers(&self) -> Vec<IoSlice<'_>> {
2374        let mut result = Vec::new();
2375
2376        for prop in self {
2377            result.append(&mut prop.to_buffers());
2378        }
2379
2380        result
2381    }
2382}
2383
2384/// Trait for calculating the total encoded size of properties collection
2385///
2386/// This trait provides functionality to calculate the total number of bytes
2387/// required to encode a collection of properties in the MQTT wire format.
2388pub trait PropertiesSize {
2389    /// Calculate the total encoded size of all properties in bytes
2390    ///
2391    /// Returns the sum of the encoded sizes of all properties in the collection.
2392    fn size(&self) -> usize;
2393}
2394
2395/// Implementation of PropertiesSize for Properties
2396///
2397/// Calculates the total size by summing the encoded size of each property.
2398impl PropertiesSize for Properties {
2399    fn size(&self) -> usize {
2400        self.iter().map(|prop| prop.size()).sum()
2401    }
2402}
2403
2404/// Trait for parsing properties collection from byte data
2405///
2406/// This trait provides functionality to parse a collection of MQTT properties
2407/// from a byte buffer according to the MQTT v5.0 specification format.
2408pub trait PropertiesParse {
2409    /// Parse properties collection from byte data
2410    ///
2411    /// Parses properties from a byte buffer that contains a variable-length integer
2412    /// indicating the properties length, followed by the encoded properties.
2413    ///
2414    /// # Parameters
2415    ///
2416    /// * `data` - Byte buffer containing the encoded properties data
2417    ///
2418    /// # Returns
2419    ///
2420    /// * `Ok((Properties, bytes_consumed))` - Successfully parsed properties and bytes consumed
2421    /// * `Err(MqttError)` - If the buffer is malformed or contains invalid property data
2422    fn parse(data: &[u8]) -> Result<(Self, usize), MqttError>
2423    where
2424        Self: Sized;
2425}
2426
2427/// Implementation of PropertiesParse for Properties
2428///
2429/// Parses properties according to MQTT v5.0 specification format:
2430/// - Variable-length integer indicating properties length
2431/// - Sequence of encoded properties
2432impl PropertiesParse for Properties {
2433    fn parse(data: &[u8]) -> Result<(Self, usize), MqttError> {
2434        if data.is_empty() {
2435            return Err(MqttError::MalformedPacket);
2436        }
2437
2438        let (prop_len, consumed) = match VariableByteInteger::decode_stream(data) {
2439            DecodeResult::Ok(vbi, cons) => (vbi, cons),
2440            _ => return Err(MqttError::MalformedPacket),
2441        };
2442
2443        let mut cursor = consumed;
2444        let mut props = Properties::new();
2445
2446        if prop_len.to_u32() == 0 {
2447            return Ok((props, cursor));
2448        }
2449
2450        let props_end = cursor + prop_len.to_u32() as usize;
2451        if props_end > data.len() {
2452            return Err(MqttError::MalformedPacket);
2453        }
2454
2455        while cursor < props_end {
2456            let (p, c) = Property::parse(&data[cursor..props_end])?;
2457            props.push(p);
2458            cursor += c;
2459        }
2460
2461        Ok((props, cursor))
2462    }
2463}