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