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