mqtt_protocol_core/mqtt/packet/
property.rs

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