mqtt_packet/
property.rs

1use crate::build_enum;
2use crate::DataType;
3use crate::Error;
4use std::collections::BTreeMap;
5use std::convert::TryFrom;
6use std::io;
7
8build_enum!(Identifier {
9  PayloadFormatIndicator = 0x01,
10  MessageExpiryInterval = 0x02,
11  ContentType = 0x03,
12  ResponseTopic = 0x08,
13  CorrelationData = 0x09,
14  SubscriptionIdentifier = 0x0b,
15  SessionExpiryInterval = 0x11,
16  AssignedClientIdentifier = 0x12,
17  ServerKeepAlive = 0x13,
18  AuthenticationMethod = 0x15,
19  AuthenticationData = 0x16,
20  RequestProblemInformation = 0x17,
21  WillDelayInterval = 0x18,
22  RequestResponseInformation = 0x19,
23  ResponseInformation = 0x1a,
24  ServerReference = 0x1c,
25  ReasonString = 0x1f,
26  ReceiveMaximum = 0x21,
27  TopicAliasMaximum = 0x22,
28  TopicAlias = 0x23,
29  MaximumQos = 0x24,
30  RetainAvailable = 0x25,
31  UserProperty = 0x26,
32  MaximumPacketSize = 0x27,
33  WildcardSubscriptionAvailable = 0x28,
34  SubscriptionIdentifierAvailable = 0x29,
35  SharedSubscriptionAvailable = 0x2a
36});
37
38/// A Property consists of an Identifier which defines its usage and data type,
39/// followed by a value.
40///
41/// # [2.2.2.2 Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901027)
42///
43/// A Property consists of an Identifier which defines its usage and data type,
44/// followed by a value. The Identifier is encoded as a Variable Byte Integer.
45/// A Control Packet which contains an Identifier which is not valid for its
46/// packet type, or contains a value not of the specified data type, is a
47/// Malformed Packet. If received, use a CONNACK or DISCONNECT packet with
48/// Reason Code 0x81 (Malformed Packet). There is no significance in the order
49/// of Properties with different Identifiers.
50pub struct Property {
51  pub values: BTreeMap<Identifier, DataType>,
52}
53
54impl Property {
55  /// Parse property identifiers and values from a reader.
56  pub fn new<R: io::Read>(reader: &mut R) -> Result<Self, Error> {
57    let length = DataType::parse_two_byte_int(reader)?;
58    let mut properties = BTreeMap::new();
59
60    for _i in 0..length.into() {
61      let identifier = Self::parse_identifier(reader)?;
62      let data_type = Self::parse_type(&identifier, reader)?;
63      properties.insert(identifier, data_type);
64    }
65
66    return Ok(Self { values: properties });
67  }
68
69  /// Parse Identifier variant from reader.
70  fn parse_identifier<R: io::Read>(reader: &mut R) -> Result<Identifier, Error> {
71    let mut id_buffer = [0; 1];
72    reader.read(&mut id_buffer)?;
73    return Ok(Identifier::try_from(id_buffer[0])?);
74  }
75
76  /// Parse property values from a reader into DataType variants.
77  fn parse_type<R: io::Read>(identifier: &Identifier, reader: &mut R) -> Result<DataType, Error> {
78    use Identifier::*;
79
80    return match identifier {
81      PayloadFormatIndicator
82      | RequestProblemInformation
83      | RequestResponseInformation
84      | MaximumQos
85      | RetainAvailable
86      | WildcardSubscriptionAvailable
87      | SubscriptionIdentifierAvailable
88      | SharedSubscriptionAvailable => DataType::parse_byte(reader),
89      ServerKeepAlive | ReceiveMaximum | TopicAliasMaximum | TopicAlias => {
90        DataType::parse_two_byte_int(reader)
91      }
92      MessageExpiryInterval | SessionExpiryInterval | WillDelayInterval | MaximumPacketSize => {
93        DataType::parse_four_byte_int(reader)
94      }
95      SubscriptionIdentifier => DataType::parse_variable_byte_int(reader),
96      UserProperty => DataType::parse_utf8_string_pair(reader),
97      CorrelationData | AuthenticationData => DataType::parse_binary_data(reader),
98      ContentType
99      | ResponseTopic
100      | AssignedClientIdentifier
101      | AuthenticationMethod
102      | ResponseInformation
103      | ServerReference
104      | ReasonString => DataType::parse_utf8_string(reader),
105    };
106  }
107
108  /// Convert Property values into a byte vector.
109  pub fn generate(&self) -> Result<Vec<u8>, Error> {
110    // we need to fit the usize into a u16, so we can grab the first two bytes
111    let length = u16::try_from(self.values.len() & 0xFFFF)
112      .unwrap()
113      .to_be_bytes()
114      .to_vec();
115
116    // create a vector to hold the generated data
117    let mut bytes = vec![];
118    bytes.push(length);
119
120    // PartialOrd sorts enum variants in the order they are declared.
121    for (key, value) in self.values.iter() {
122      let id: u8 = u8::from(*key);
123      bytes.push(vec![id]);
124      bytes.push(value.into_bytes()?);
125    }
126
127    return Ok(bytes.concat());
128  }
129}