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