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