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)]
1756pub enum Property {
1757    PayloadFormatIndicator(PayloadFormatIndicator),
1758    MessageExpiryInterval(MessageExpiryInterval),
1759    ContentType(ContentType),
1760    ResponseTopic(ResponseTopic),
1761    CorrelationData(CorrelationData),
1762    SubscriptionIdentifier(SubscriptionIdentifier),
1763    SessionExpiryInterval(SessionExpiryInterval),
1764    AssignedClientIdentifier(AssignedClientIdentifier),
1765    ServerKeepAlive(ServerKeepAlive),
1766    AuthenticationMethod(AuthenticationMethod),
1767    AuthenticationData(AuthenticationData),
1768    RequestProblemInformation(RequestProblemInformation),
1769    WillDelayInterval(WillDelayInterval),
1770    RequestResponseInformation(RequestResponseInformation),
1771    ResponseInformation(ResponseInformation),
1772    ServerReference(ServerReference),
1773    ReasonString(ReasonString),
1774    ReceiveMaximum(ReceiveMaximum),
1775    TopicAliasMaximum(TopicAliasMaximum),
1776    TopicAlias(TopicAlias),
1777    MaximumQos(MaximumQos),
1778    RetainAvailable(RetainAvailable),
1779    UserProperty(UserProperty),
1780    MaximumPacketSize(MaximumPacketSize),
1781    WildcardSubscriptionAvailable(WildcardSubscriptionAvailable),
1782    SubscriptionIdentifierAvailable(SubscriptionIdentifierAvailable),
1783    SharedSubscriptionAvailable(SharedSubscriptionAvailable),
1784}
1785
1786impl fmt::Display for Property {
1787    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1788        match self {
1789            Property::PayloadFormatIndicator(p) => write!(f, "{p}"),
1790            Property::MessageExpiryInterval(p) => write!(f, "{p}"),
1791            Property::ContentType(p) => write!(f, "{p}"),
1792            Property::ResponseTopic(p) => write!(f, "{p}"),
1793            Property::CorrelationData(p) => write!(f, "{p}"),
1794            Property::SubscriptionIdentifier(p) => write!(f, "{p}"),
1795            Property::SessionExpiryInterval(p) => write!(f, "{p}"),
1796            Property::AssignedClientIdentifier(p) => write!(f, "{p}"),
1797            Property::ServerKeepAlive(p) => write!(f, "{p}"),
1798            Property::AuthenticationMethod(p) => write!(f, "{p}"),
1799            Property::AuthenticationData(p) => write!(f, "{p}"),
1800            Property::RequestProblemInformation(p) => write!(f, "{p}"),
1801            Property::WillDelayInterval(p) => write!(f, "{p}"),
1802            Property::RequestResponseInformation(p) => write!(f, "{p}"),
1803            Property::ResponseInformation(p) => write!(f, "{p}"),
1804            Property::ServerReference(p) => write!(f, "{p}"),
1805            Property::ReasonString(p) => write!(f, "{p}"),
1806            Property::ReceiveMaximum(p) => write!(f, "{p}"),
1807            Property::TopicAliasMaximum(p) => write!(f, "{p}"),
1808            Property::TopicAlias(p) => write!(f, "{p}"),
1809            Property::MaximumQos(p) => write!(f, "{p}"),
1810            Property::RetainAvailable(p) => write!(f, "{p}"),
1811            Property::UserProperty(p) => write!(f, "{p}"),
1812            Property::MaximumPacketSize(p) => write!(f, "{p}"),
1813            Property::WildcardSubscriptionAvailable(p) => write!(f, "{p}"),
1814            Property::SubscriptionIdentifierAvailable(p) => write!(f, "{p}"),
1815            Property::SharedSubscriptionAvailable(p) => write!(f, "{p}"),
1816        }
1817    }
1818}
1819
1820/// Trait for accessing property values in a type-safe manner
1821///
1822/// This trait provides methods to extract values from `Property` enum variants
1823/// without having to match on each variant explicitly. Methods return `Option`
1824/// to handle cases where the property type doesn't match the requested type.
1825///
1826/// # Examples
1827///
1828/// ```ignore
1829/// use mqtt_protocol_core::mqtt;
1830///
1831/// let prop = mqtt::packet::Property::MessageExpiryInterval(
1832///     mqtt::packet::MessageExpiryInterval::new(3600).unwrap()
1833/// );
1834///
1835/// // Extract the u32 value
1836/// if let Some(interval) = prop.as_u32() {
1837///     println!("Message expires in {} seconds", interval);
1838/// }
1839/// ```
1840pub trait PropertyValueAccess {
1841    /// Extract u8 value from byte-based properties
1842    ///
1843    /// Returns `Some(u8)` for properties that store single-byte values,
1844    /// `None` for other property types.
1845    fn as_u8(&self) -> Option<u8>;
1846
1847    /// Extract u16 value from two-byte properties
1848    ///
1849    /// Returns `Some(u16)` for properties that store two-byte values,
1850    /// `None` for other property types.
1851    fn as_u16(&self) -> Option<u16>;
1852
1853    /// Extract u32 value from four-byte properties
1854    ///
1855    /// Returns `Some(u32)` for properties that store four-byte values,
1856    /// `None` for other property types.
1857    fn as_u32(&self) -> Option<u32>;
1858
1859    /// Extract string value from string-based properties
1860    ///
1861    /// Returns `Some(&str)` for properties that store UTF-8 strings,
1862    /// `None` for other property types.
1863    fn as_str(&self) -> Option<&str>;
1864
1865    /// Extract binary data from binary-based properties
1866    ///
1867    /// Returns `Some(&[u8])` for properties that store binary data,
1868    /// `None` for other property types.
1869    fn as_bytes(&self) -> Option<&[u8]>;
1870
1871    /// Extract key-value pair from UserProperty
1872    ///
1873    /// Returns `Some((key, value))` for UserProperty, `None` for other property types.
1874    fn as_key_value(&self) -> Option<(&str, &str)>;
1875}
1876
1877impl PropertyValueAccess for Property {
1878    fn as_u8(&self) -> Option<u8> {
1879        match self {
1880            // All property types that return u8
1881            Property::PayloadFormatIndicator(p) => Some(p.val()),
1882            Property::MaximumQos(p) => Some(p.val()),
1883            Property::RetainAvailable(p) => Some(p.val()),
1884            Property::RequestProblemInformation(p) => Some(p.val()),
1885            Property::RequestResponseInformation(p) => Some(p.val()),
1886            Property::WildcardSubscriptionAvailable(p) => Some(p.val()),
1887            Property::SubscriptionIdentifierAvailable(p) => Some(p.val()),
1888            Property::SharedSubscriptionAvailable(p) => Some(p.val()),
1889            _ => None,
1890        }
1891    }
1892
1893    fn as_u16(&self) -> Option<u16> {
1894        match self {
1895            // All property types that return u16
1896            Property::TopicAlias(p) => Some(p.val()),
1897            Property::ReceiveMaximum(p) => Some(p.val()),
1898            Property::TopicAliasMaximum(p) => Some(p.val()),
1899            Property::ServerKeepAlive(p) => Some(p.val()),
1900            _ => None,
1901        }
1902    }
1903
1904    fn as_u32(&self) -> Option<u32> {
1905        match self {
1906            // All property types that return u32
1907            Property::MessageExpiryInterval(p) => Some(p.val()),
1908            Property::SessionExpiryInterval(p) => Some(p.val()),
1909            Property::WillDelayInterval(p) => Some(p.val()),
1910            Property::MaximumPacketSize(p) => Some(p.val()),
1911            Property::SubscriptionIdentifier(p) => Some(p.val()),
1912            _ => None,
1913        }
1914    }
1915
1916    fn as_str(&self) -> Option<&str> {
1917        match self {
1918            // All property types that return strings
1919            Property::ContentType(p) => Some(p.val()),
1920            Property::ResponseTopic(p) => Some(p.val()),
1921            Property::AssignedClientIdentifier(p) => Some(p.val()),
1922            Property::AuthenticationMethod(p) => Some(p.val()),
1923            Property::ResponseInformation(p) => Some(p.val()),
1924            Property::ServerReference(p) => Some(p.val()),
1925            Property::ReasonString(p) => Some(p.val()),
1926            _ => None,
1927        }
1928    }
1929
1930    fn as_bytes(&self) -> Option<&[u8]> {
1931        match self {
1932            // Property types that return binary data
1933            Property::CorrelationData(p) => Some(p.val()),
1934            Property::AuthenticationData(p) => Some(p.val()),
1935            _ => None,
1936        }
1937    }
1938
1939    fn as_key_value(&self) -> Option<(&str, &str)> {
1940        match self {
1941            // Property types that return key-value pairs
1942            Property::UserProperty(p) => Some((p.key(), p.val())),
1943            _ => None,
1944        }
1945    }
1946}
1947
1948impl Property {
1949    /// Get the property identifier for this property
1950    ///
1951    /// Returns the `PropertyId` that corresponds to this property type.
1952    /// This is useful for determining the property type without matching
1953    /// on the enum variant.
1954    ///
1955    /// # Examples
1956    ///
1957    /// ```ignore
1958    /// use mqtt_protocol_core::mqtt;
1959    ///
1960    /// let prop = mqtt::packet::Property::MessageExpiryInterval(
1961    ///     mqtt::packet::MessageExpiryInterval::new(3600).unwrap()
1962    /// );
1963    /// assert_eq!(prop.id(), mqtt::packet::PropertyId::MessageExpiryInterval);
1964    /// ```
1965    pub fn id(&self) -> PropertyId {
1966        match self {
1967            Property::PayloadFormatIndicator(p) => p.id(),
1968            Property::MessageExpiryInterval(p) => p.id(),
1969            Property::ContentType(p) => p.id(),
1970            Property::ResponseTopic(p) => p.id(),
1971            Property::CorrelationData(p) => p.id(),
1972            Property::SubscriptionIdentifier(p) => p.id(),
1973            Property::SessionExpiryInterval(p) => p.id(),
1974            Property::AssignedClientIdentifier(p) => p.id(),
1975            Property::ServerKeepAlive(p) => p.id(),
1976            Property::AuthenticationMethod(p) => p.id(),
1977            Property::AuthenticationData(p) => p.id(),
1978            Property::RequestProblemInformation(p) => p.id(),
1979            Property::WillDelayInterval(p) => p.id(),
1980            Property::RequestResponseInformation(p) => p.id(),
1981            Property::ResponseInformation(p) => p.id(),
1982            Property::ServerReference(p) => p.id(),
1983            Property::ReasonString(p) => p.id(),
1984            Property::ReceiveMaximum(p) => p.id(),
1985            Property::TopicAliasMaximum(p) => p.id(),
1986            Property::TopicAlias(p) => p.id(),
1987            Property::MaximumQos(p) => p.id(),
1988            Property::RetainAvailable(p) => p.id(),
1989            Property::UserProperty(p) => p.id(),
1990            Property::MaximumPacketSize(p) => p.id(),
1991            Property::WildcardSubscriptionAvailable(p) => p.id(),
1992            Property::SubscriptionIdentifierAvailable(p) => p.id(),
1993            Property::SharedSubscriptionAvailable(p) => p.id(),
1994        }
1995    }
1996
1997    /// Get the encoded size of this property in bytes
1998    ///
1999    /// Returns the total number of bytes required to encode this property
2000    /// in the MQTT wire format, including the property identifier and
2001    /// any length prefixes.
2002    ///
2003    /// # Examples
2004    ///
2005    /// ```ignore
2006    /// use mqtt_protocol_core::mqtt;
2007    ///
2008    /// let prop = mqtt::packet::Property::MessageExpiryInterval(
2009    ///     mqtt::packet::MessageExpiryInterval::new(3600).unwrap()
2010    /// );
2011    /// let size = prop.size(); // 1 byte ID + 4 bytes value = 5 bytes
2012    /// ```
2013    pub fn size(&self) -> usize {
2014        match self {
2015            Property::PayloadFormatIndicator(p) => p.size(),
2016            Property::MessageExpiryInterval(p) => p.size(),
2017            Property::ContentType(p) => p.size(),
2018            Property::ResponseTopic(p) => p.size(),
2019            Property::CorrelationData(p) => p.size(),
2020            Property::SubscriptionIdentifier(p) => p.size(),
2021            Property::SessionExpiryInterval(p) => p.size(),
2022            Property::AssignedClientIdentifier(p) => p.size(),
2023            Property::ServerKeepAlive(p) => p.size(),
2024            Property::AuthenticationMethod(p) => p.size(),
2025            Property::AuthenticationData(p) => p.size(),
2026            Property::RequestProblemInformation(p) => p.size(),
2027            Property::WillDelayInterval(p) => p.size(),
2028            Property::RequestResponseInformation(p) => p.size(),
2029            Property::ResponseInformation(p) => p.size(),
2030            Property::ServerReference(p) => p.size(),
2031            Property::ReasonString(p) => p.size(),
2032            Property::ReceiveMaximum(p) => p.size(),
2033            Property::TopicAliasMaximum(p) => p.size(),
2034            Property::TopicAlias(p) => p.size(),
2035            Property::MaximumQos(p) => p.size(),
2036            Property::RetainAvailable(p) => p.size(),
2037            Property::UserProperty(p) => p.size(),
2038            Property::MaximumPacketSize(p) => p.size(),
2039            Property::WildcardSubscriptionAvailable(p) => p.size(),
2040            Property::SubscriptionIdentifierAvailable(p) => p.size(),
2041            Property::SharedSubscriptionAvailable(p) => p.size(),
2042        }
2043    }
2044
2045    /// Create IoSlice buffers for efficient network I/O
2046    ///
2047    /// Returns a vector of `IoSlice` objects that can be used for vectored I/O
2048    /// operations, allowing zero-copy writes to network sockets. The buffers
2049    /// include the property identifier and the encoded property value.
2050    ///
2051    /// # Examples
2052    ///
2053    /// ```ignore
2054    /// use mqtt_protocol_core::mqtt;
2055    ///
2056    /// let prop = mqtt::packet::Property::ContentType(
2057    ///     mqtt::packet::ContentType::new("application/json").unwrap()
2058    /// );
2059    /// let buffers = prop.to_buffers();
2060    /// // Can be used with vectored write operations
2061    /// // socket.write_vectored(&buffers)?;
2062    /// ```
2063    #[cfg(feature = "std")]
2064    pub fn to_buffers(&self) -> Vec<IoSlice<'_>> {
2065        match self {
2066            Property::PayloadFormatIndicator(p) => p.to_buffers(),
2067            Property::MessageExpiryInterval(p) => p.to_buffers(),
2068            Property::ContentType(p) => p.to_buffers(),
2069            Property::ResponseTopic(p) => p.to_buffers(),
2070            Property::CorrelationData(p) => p.to_buffers(),
2071            Property::SubscriptionIdentifier(p) => p.to_buffers(),
2072            Property::SessionExpiryInterval(p) => p.to_buffers(),
2073            Property::AssignedClientIdentifier(p) => p.to_buffers(),
2074            Property::ServerKeepAlive(p) => p.to_buffers(),
2075            Property::AuthenticationMethod(p) => p.to_buffers(),
2076            Property::AuthenticationData(p) => p.to_buffers(),
2077            Property::RequestProblemInformation(p) => p.to_buffers(),
2078            Property::WillDelayInterval(p) => p.to_buffers(),
2079            Property::RequestResponseInformation(p) => p.to_buffers(),
2080            Property::ResponseInformation(p) => p.to_buffers(),
2081            Property::ServerReference(p) => p.to_buffers(),
2082            Property::ReasonString(p) => p.to_buffers(),
2083            Property::ReceiveMaximum(p) => p.to_buffers(),
2084            Property::TopicAliasMaximum(p) => p.to_buffers(),
2085            Property::TopicAlias(p) => p.to_buffers(),
2086            Property::MaximumQos(p) => p.to_buffers(),
2087            Property::RetainAvailable(p) => p.to_buffers(),
2088            Property::UserProperty(p) => p.to_buffers(),
2089            Property::MaximumPacketSize(p) => p.to_buffers(),
2090            Property::WildcardSubscriptionAvailable(p) => p.to_buffers(),
2091            Property::SubscriptionIdentifierAvailable(p) => p.to_buffers(),
2092            Property::SharedSubscriptionAvailable(p) => p.to_buffers(),
2093        }
2094    }
2095
2096    /// Create a continuous buffer containing the complete property data
2097    ///
2098    /// Returns a vector containing all property bytes in a single continuous buffer.
2099    /// This method is compatible with no-std environments and provides an alternative
2100    /// to [`to_buffers()`] when vectored I/O is not needed.
2101    ///
2102    /// The returned buffer includes the property identifier and the encoded property value.
2103    ///
2104    /// # Examples
2105    ///
2106    /// ```ignore
2107    /// use mqtt_protocol_core::mqtt;
2108    ///
2109    /// let prop = mqtt::packet::Property::ContentType(
2110    ///     mqtt::packet::ContentType::new("application/json").unwrap()
2111    /// );
2112    /// let buffer = prop.to_continuous_buffer();
2113    /// // buffer contains all property bytes
2114    /// ```
2115    ///
2116    /// [`to_buffers()`]: #method.to_buffers
2117    pub fn to_continuous_buffer(&self) -> Vec<u8> {
2118        match self {
2119            Property::PayloadFormatIndicator(p) => p.to_continuous_buffer(),
2120            Property::MessageExpiryInterval(p) => p.to_continuous_buffer(),
2121            Property::ContentType(p) => p.to_continuous_buffer(),
2122            Property::ResponseTopic(p) => p.to_continuous_buffer(),
2123            Property::CorrelationData(p) => p.to_continuous_buffer(),
2124            Property::SubscriptionIdentifier(p) => p.to_continuous_buffer(),
2125            Property::SessionExpiryInterval(p) => p.to_continuous_buffer(),
2126            Property::AssignedClientIdentifier(p) => p.to_continuous_buffer(),
2127            Property::ServerKeepAlive(p) => p.to_continuous_buffer(),
2128            Property::AuthenticationMethod(p) => p.to_continuous_buffer(),
2129            Property::AuthenticationData(p) => p.to_continuous_buffer(),
2130            Property::RequestProblemInformation(p) => p.to_continuous_buffer(),
2131            Property::WillDelayInterval(p) => p.to_continuous_buffer(),
2132            Property::RequestResponseInformation(p) => p.to_continuous_buffer(),
2133            Property::ResponseInformation(p) => p.to_continuous_buffer(),
2134            Property::ServerReference(p) => p.to_continuous_buffer(),
2135            Property::ReasonString(p) => p.to_continuous_buffer(),
2136            Property::ReceiveMaximum(p) => p.to_continuous_buffer(),
2137            Property::TopicAliasMaximum(p) => p.to_continuous_buffer(),
2138            Property::TopicAlias(p) => p.to_continuous_buffer(),
2139            Property::MaximumQos(p) => p.to_continuous_buffer(),
2140            Property::RetainAvailable(p) => p.to_continuous_buffer(),
2141            Property::UserProperty(p) => p.to_continuous_buffer(),
2142            Property::MaximumPacketSize(p) => p.to_continuous_buffer(),
2143            Property::WildcardSubscriptionAvailable(p) => p.to_continuous_buffer(),
2144            Property::SubscriptionIdentifierAvailable(p) => p.to_continuous_buffer(),
2145            Property::SharedSubscriptionAvailable(p) => p.to_continuous_buffer(),
2146        }
2147    }
2148
2149    /// Parse a property from a byte sequence
2150    ///
2151    /// Decodes a single MQTT property from a byte buffer according to the MQTT v5.0
2152    /// specification. The buffer must start with a property identifier byte followed
2153    /// by the property value in the appropriate format.
2154    ///
2155    /// # Parameters
2156    ///
2157    /// * `bytes` - Byte buffer containing the encoded property data
2158    ///
2159    /// # Returns
2160    ///
2161    /// * `Ok((Property, bytes_consumed))` - Successfully parsed property and number of bytes consumed
2162    /// * `Err(MqttError::MalformedPacket)` - If the buffer is too short, contains an invalid property ID, or malformed property data
2163    ///
2164    /// # Examples
2165    ///
2166    /// ```ignore
2167    /// use mqtt_protocol_core::mqtt;
2168    ///
2169    /// // Buffer: [property_id, property_data...]
2170    /// let buffer = &[0x02, 0x00, 0x00, 0x0E, 0x10]; // MessageExpiryInterval = 3600
2171    /// let (property, consumed) = mqtt::packet::Property::parse(buffer).unwrap();
2172    ///
2173    /// match property {
2174    ///     mqtt::packet::Property::MessageExpiryInterval(prop) => {
2175    ///         assert_eq!(prop.val(), 3600);
2176    ///     }
2177    ///     _ => panic!("Wrong property type"),
2178    /// }
2179    /// assert_eq!(consumed, 5);
2180    /// ```
2181    pub fn parse(bytes: &[u8]) -> Result<(Self, usize), MqttError> {
2182        if bytes.is_empty() {
2183            return Err(MqttError::MalformedPacket);
2184        }
2185
2186        let id = PropertyId::try_from(bytes[0]).map_err(|_| MqttError::MalformedPacket)?;
2187
2188        let (prop, len) = match id {
2189            PropertyId::PayloadFormatIndicator => {
2190                let (p, l) = PayloadFormatIndicator::parse(&bytes[1..])?;
2191                (Self::PayloadFormatIndicator(p), l + 1)
2192            }
2193            PropertyId::MessageExpiryInterval => {
2194                let (p, l) = MessageExpiryInterval::parse(&bytes[1..])?;
2195                (Self::MessageExpiryInterval(p), l + 1)
2196            }
2197            PropertyId::ContentType => {
2198                let (p, l) = ContentType::parse(&bytes[1..])?;
2199                (Self::ContentType(p), l + 1)
2200            }
2201            PropertyId::ResponseTopic => {
2202                let (p, l) = ResponseTopic::parse(&bytes[1..])?;
2203                (Self::ResponseTopic(p), l + 1)
2204            }
2205            PropertyId::CorrelationData => {
2206                let (p, l) = CorrelationData::parse(&bytes[1..])?;
2207                (Self::CorrelationData(p), l + 1)
2208            }
2209            PropertyId::SubscriptionIdentifier => {
2210                let (p, l) = SubscriptionIdentifier::parse(&bytes[1..])?;
2211                (Self::SubscriptionIdentifier(p), l + 1)
2212            }
2213            PropertyId::SessionExpiryInterval => {
2214                let (p, l) = SessionExpiryInterval::parse(&bytes[1..])?;
2215                (Self::SessionExpiryInterval(p), l + 1)
2216            }
2217            PropertyId::AssignedClientIdentifier => {
2218                let (p, l) = AssignedClientIdentifier::parse(&bytes[1..])?;
2219                (Self::AssignedClientIdentifier(p), l + 1)
2220            }
2221            PropertyId::ServerKeepAlive => {
2222                let (p, l) = ServerKeepAlive::parse(&bytes[1..])?;
2223                (Self::ServerKeepAlive(p), l + 1)
2224            }
2225            PropertyId::AuthenticationMethod => {
2226                let (p, l) = AuthenticationMethod::parse(&bytes[1..])?;
2227                (Self::AuthenticationMethod(p), l + 1)
2228            }
2229            PropertyId::AuthenticationData => {
2230                let (p, l) = AuthenticationData::parse(&bytes[1..])?;
2231                (Self::AuthenticationData(p), l + 1)
2232            }
2233            PropertyId::RequestProblemInformation => {
2234                let (p, l) = RequestProblemInformation::parse(&bytes[1..])?;
2235                (Self::RequestProblemInformation(p), l + 1)
2236            }
2237            PropertyId::WillDelayInterval => {
2238                let (p, l) = WillDelayInterval::parse(&bytes[1..])?;
2239                (Self::WillDelayInterval(p), l + 1)
2240            }
2241            PropertyId::RequestResponseInformation => {
2242                let (p, l) = RequestResponseInformation::parse(&bytes[1..])?;
2243                (Self::RequestResponseInformation(p), l + 1)
2244            }
2245            PropertyId::ResponseInformation => {
2246                let (p, l) = ResponseInformation::parse(&bytes[1..])?;
2247                (Self::ResponseInformation(p), l + 1)
2248            }
2249            PropertyId::ServerReference => {
2250                let (p, l) = ServerReference::parse(&bytes[1..])?;
2251                (Self::ServerReference(p), l + 1)
2252            }
2253            PropertyId::ReasonString => {
2254                let (p, l) = ReasonString::parse(&bytes[1..])?;
2255                (Self::ReasonString(p), l + 1)
2256            }
2257            PropertyId::ReceiveMaximum => {
2258                let (p, l) = ReceiveMaximum::parse(&bytes[1..])?;
2259                (Self::ReceiveMaximum(p), l + 1)
2260            }
2261            PropertyId::TopicAliasMaximum => {
2262                let (p, l) = TopicAliasMaximum::parse(&bytes[1..])?;
2263                (Self::TopicAliasMaximum(p), l + 1)
2264            }
2265            PropertyId::TopicAlias => {
2266                let (p, l) = TopicAlias::parse(&bytes[1..])?;
2267                (Self::TopicAlias(p), l + 1)
2268            }
2269            PropertyId::MaximumQos => {
2270                let (p, l) = MaximumQos::parse(&bytes[1..])?;
2271                (Self::MaximumQos(p), l + 1)
2272            }
2273            PropertyId::RetainAvailable => {
2274                let (p, l) = RetainAvailable::parse(&bytes[1..])?;
2275                (Self::RetainAvailable(p), l + 1)
2276            }
2277            PropertyId::UserProperty => {
2278                let (p, l) = UserProperty::parse(&bytes[1..])?;
2279                (Self::UserProperty(p), l + 1)
2280            }
2281            PropertyId::MaximumPacketSize => {
2282                let (p, l) = MaximumPacketSize::parse(&bytes[1..])?;
2283                (Self::MaximumPacketSize(p), l + 1)
2284            }
2285            PropertyId::WildcardSubscriptionAvailable => {
2286                let (p, l) = WildcardSubscriptionAvailable::parse(&bytes[1..])?;
2287                (Self::WildcardSubscriptionAvailable(p), l + 1)
2288            }
2289            PropertyId::SubscriptionIdentifierAvailable => {
2290                let (p, l) = SubscriptionIdentifierAvailable::parse(&bytes[1..])?;
2291                (Self::SubscriptionIdentifierAvailable(p), l + 1)
2292            }
2293            PropertyId::SharedSubscriptionAvailable => {
2294                let (p, l) = SharedSubscriptionAvailable::parse(&bytes[1..])?;
2295                (Self::SharedSubscriptionAvailable(p), l + 1)
2296            }
2297        };
2298
2299        Ok((prop, len))
2300    }
2301}
2302
2303/// Collection of MQTT properties
2304///
2305/// This type alias represents a collection of MQTT v5.0 properties that can be
2306/// included in various packet types. Properties are stored as a vector to preserve
2307/// order and allow multiple instances of certain property types (like UserProperty).
2308///
2309/// # Examples
2310///
2311/// ```ignore
2312/// use mqtt_protocol_core::mqtt;
2313///
2314/// let mut properties = mqtt::packet::Properties::new();
2315///
2316/// // Add a message expiry interval
2317/// let expiry = mqtt::packet::MessageExpiryInterval::new(3600).unwrap();
2318/// properties.push(mqtt::packet::Property::MessageExpiryInterval(expiry));
2319///
2320/// // Add user-defined properties
2321/// let user_prop = mqtt::packet::UserProperty::new("app", "myapp").unwrap();
2322/// properties.push(mqtt::packet::Property::UserProperty(user_prop));
2323/// ```
2324pub type Properties = Vec<Property>;
2325
2326/// Trait for converting properties collection to continuous buffer
2327///
2328/// This trait provides functionality to convert a collection of properties
2329/// into a single continuous buffer compatible with no-std environments.
2330pub trait PropertiesToContinuousBuffer {
2331    /// Convert properties to continuous buffer
2332    ///
2333    /// Returns a vector containing all property bytes in a single continuous buffer.
2334    fn to_continuous_buffer(&self) -> Vec<u8>;
2335}
2336
2337/// Trait for converting properties collection to I/O buffers
2338///
2339/// This trait provides functionality to convert a collection of properties
2340/// into IoSlice buffers suitable for efficient network I/O operations.
2341#[cfg(feature = "std")]
2342pub trait PropertiesToBuffers {
2343    /// Convert properties to IoSlice buffers for vectored I/O
2344    ///
2345    /// Returns a vector of IoSlice objects that can be used with
2346    /// vectored write operations for zero-copy network transmission.
2347    fn to_buffers(&self) -> Vec<IoSlice<'_>>;
2348}
2349
2350/// Implementation of PropertiesToContinuousBuffer for Properties
2351///
2352/// Concatenates continuous buffers from all properties in the collection.
2353impl PropertiesToContinuousBuffer for Properties {
2354    fn to_continuous_buffer(&self) -> Vec<u8> {
2355        let mut result = Vec::new();
2356
2357        for prop in self {
2358            result.append(&mut prop.to_continuous_buffer());
2359        }
2360
2361        result
2362    }
2363}
2364
2365/// Implementation of PropertiesToBuffers for Properties
2366///
2367/// Concatenates IoSlice buffers from all properties in the collection.
2368#[cfg(feature = "std")]
2369impl PropertiesToBuffers for Properties {
2370    fn to_buffers(&self) -> Vec<IoSlice<'_>> {
2371        let mut result = Vec::new();
2372
2373        for prop in self {
2374            result.append(&mut prop.to_buffers());
2375        }
2376
2377        result
2378    }
2379}
2380
2381/// Trait for calculating the total encoded size of properties collection
2382///
2383/// This trait provides functionality to calculate the total number of bytes
2384/// required to encode a collection of properties in the MQTT wire format.
2385pub trait PropertiesSize {
2386    /// Calculate the total encoded size of all properties in bytes
2387    ///
2388    /// Returns the sum of the encoded sizes of all properties in the collection.
2389    fn size(&self) -> usize;
2390}
2391
2392/// Implementation of PropertiesSize for Properties
2393///
2394/// Calculates the total size by summing the encoded size of each property.
2395impl PropertiesSize for Properties {
2396    fn size(&self) -> usize {
2397        self.iter().map(|prop| prop.size()).sum()
2398    }
2399}
2400
2401/// Trait for parsing properties collection from byte data
2402///
2403/// This trait provides functionality to parse a collection of MQTT properties
2404/// from a byte buffer according to the MQTT v5.0 specification format.
2405pub trait PropertiesParse {
2406    /// Parse properties collection from byte data
2407    ///
2408    /// Parses properties from a byte buffer that contains a variable-length integer
2409    /// indicating the properties length, followed by the encoded properties.
2410    ///
2411    /// # Parameters
2412    ///
2413    /// * `data` - Byte buffer containing the encoded properties data
2414    ///
2415    /// # Returns
2416    ///
2417    /// * `Ok((Properties, bytes_consumed))` - Successfully parsed properties and bytes consumed
2418    /// * `Err(MqttError)` - If the buffer is malformed or contains invalid property data
2419    fn parse(data: &[u8]) -> Result<(Self, usize), MqttError>
2420    where
2421        Self: Sized;
2422}
2423
2424/// Implementation of PropertiesParse for Properties
2425///
2426/// Parses properties according to MQTT v5.0 specification format:
2427/// - Variable-length integer indicating properties length
2428/// - Sequence of encoded properties
2429impl PropertiesParse for Properties {
2430    fn parse(data: &[u8]) -> Result<(Self, usize), MqttError> {
2431        if data.is_empty() {
2432            return Err(MqttError::MalformedPacket);
2433        }
2434
2435        let (prop_len, consumed) = match VariableByteInteger::decode_stream(data) {
2436            DecodeResult::Ok(vbi, cons) => (vbi, cons),
2437            _ => return Err(MqttError::MalformedPacket),
2438        };
2439
2440        let mut cursor = consumed;
2441        let mut props = Properties::new();
2442
2443        if prop_len.to_u32() == 0 {
2444            return Ok((props, cursor));
2445        }
2446
2447        let props_end = cursor + prop_len.to_u32() as usize;
2448        if props_end > data.len() {
2449            return Err(MqttError::MalformedPacket);
2450        }
2451
2452        while cursor < props_end {
2453            let (p, c) = Property::parse(&data[cursor..props_end])?;
2454            props.push(p);
2455            cursor += c;
2456        }
2457
2458        Ok((props, cursor))
2459    }
2460}