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