### Get Type ID Source: https://docs.rs/mqttbytes/0.6.0/mqttbytes/v5/struct.PubRel.html Retrieves the `TypeId` of the PubRel struct. ```rust fn type_id(&self) -> TypeId> ``` -------------------------------- ### Sample Publish Packet for V5 Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/publish.rs.html Creates a sample MQTT Publish packet conforming to V5 specifications, including various properties like payload format indicator, message expiry interval, topic alias, response topic, correlation data, user properties, subscription identifiers, and content type. This is used for testing encoding and decoding. ```rust fn sample_v5() -> Publish { let publish_properties = PublishProperties { payload_format_indicator: Some(1), message_expiry_interval: Some(4321), topic_alias: Some(100), response_topic: Some("topic".to_owned()), correlation_data: Some(Bytes::from(vec![1, 2, 3, 4])), user_properties: vec![("test".to_owned(), "test".to_owned())], subscription_identifiers: vec![120, 121], content_type: Some("test".to_owned()), }; Publish { dup: false, qos: QoS::ExactlyOnce, retain: false, topic: "test".to_string(), pkid: 42, properties: Some(publish_properties), payload: Bytes::from(vec![b't', b'e', b's', b't']), } } ``` -------------------------------- ### Test Unsubscribe Packet Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/unsubscribe.rs.html Unit test setup for verifying Unsubscribe packet functionality. ```rust #[cfg(test)] mod test { use super::*; use alloc::vec; use bytes::BytesMut; use pretty_assertions::assert_eq; fn sample() -> Unsubscribe { let properties = UnsubscribeProperties { ``` -------------------------------- ### Create New Connect Instance Source: https://docs.rs/mqttbytes/0.6.0/mqttbytes/v4/struct.Connect.html Constructs a new `Connect` struct with a mandatory client ID. Use this to initiate a new MQTT connection. ```rust pub fn new>(id: S) -> Connect ``` -------------------------------- ### Create Empty Subscribe Packet Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/subscribe.rs.html Use `Subscribe::empty_subscribe` to create a subscribe packet with no filters. This can be used as a starting point before adding filters. ```rust pub fn empty_subscribe() -> Subscribe { Subscribe { pkid: 0, filters: Vec::new(), properties: None, } } ``` -------------------------------- ### Sample Connect Packet Creation in Rust Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/connect.rs.html Creates a sample `Connect` packet including `ConnectProperties` and `WillProperties`. This is used for testing the serialization and deserialization logic. ```rust fn sample() -> Connect { let connect_properties = ConnectProperties { session_expiry_interval: Some(1234), receive_maximum: Some(432), max_packet_size: Some(100), topic_alias_max: Some(456), request_response_info: Some(1), request_problem_info: Some(1), user_properties: vec![("test".to_owned(), "test".to_owned())], authentication_method: Some("test".to_owned()), authentication_data: Some(Bytes::from(vec![1, 2, 3, 4])), }; let will_properties = WillProperties { delay_interval: Some(1234), payload_format_indicator: Some(0), message_expiry_interval: Some(4321), content_type: Some("test".to_owned()), response_topic: Some("topic".to_owned()), correlation_data: Some(Bytes::from(vec![1, 2, 3, 4])), user_properties: vec![("test".to_owned(), "test".to_owned())], }; let will = LastWill { topic: "mydevice/status".to_string(), message: Bytes::from(vec![b'd', b'e', b'a', b'd']), qos: QoS::AtMostOnce, ``` -------------------------------- ### Sample Subscribe Packet Creation (No Properties) Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/subscribe.rs.html Creates a sample Subscribe packet without properties for testing. Includes a filter and QoS level. ```rust fn sample2() -> Subscribe { let filter = SubscribeFilter::new("hello/world".to_owned(), QoS::AtLeastOnce); Subscribe { pkid: 42, filters: vec![filter], properties: None, } } ``` -------------------------------- ### Define MQTT Connect Sample Data Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/connect.rs.html Sample structures representing MQTT Connect packets for testing purposes. ```rust let login = Login { username: "matteo".to_string(), password: "collina".to_string(), }; Connect { protocol: Protocol::V5, keep_alive: 0, properties: Some(connect_properties), client_id: "my-device".to_string(), clean_session: true, last_will: Some(will), login: Some(login), } } ``` ```rust fn sample2() -> Connect { Connect { protocol: Protocol::V5, keep_alive: 10, properties: None, client_id: "hackathonmqtt5test".to_owned(), clean_session: true, last_will: None, login: None, } } ``` -------------------------------- ### mqttbytes Crate Overview Source: https://docs.rs/mqttbytes/0.6.0/index.html Overview of the mqttbytes crate, including its modules, structs, enums, and functions. ```APIDOC ## Crate mqttbytes ### Summary This crate provides functionality for working with MQTT bytes, including packet parsing, topic matching, and protocol version handling. ### Modules * `v4`: Module for MQTT v4 specific functionality. * `v5`: Module for MQTT v5 specific functionality. ### Structs * **FixedHeader** * Description: Represents the fixed header of an MQTT packet, containing the packet type and flags. ### Enums * **Error** * Description: Represents errors that can occur during serialization and deserialization of MQTT packets. * **PacketType** * Description: Enumerates the different types of MQTT packets. * **Protocol** * Description: Represents the MQTT protocol version. * **QoS** * Description: Represents the Quality of Service levels in MQTT. ### Functions * **check** * Description: Checks if a stream has enough bytes to form a complete MQTT packet header. It returns the `FixedHeader` if a packet can be framed without modifying the parent stream's cursor. If an error occurs, subsequent checks on the same stream will restart from the beginning. * **has_wildcards** * Description: Determines if a given topic or topic filter string contains wildcard characters. * **matches** * Description: Checks if a topic string matches a given topic filter string. Note that topic and filter validation is not performed by this function. * **qos** * Description: Maps a numerical value to its corresponding `QoS` enum variant. * **valid_filter** * Description: Validates if a given string is a valid MQTT topic filter. * **valid_topic** * Description: Validates if a given string is a valid MQTT topic. ``` -------------------------------- ### Create New Login Instance Source: https://docs.rs/mqttbytes/0.6.0/mqttbytes/v4/struct.Login.html Constructs a new Login instance. Accepts any type that can be converted into a String for username and password. ```rust pub fn new>(u: S, p: S) -> Login ``` -------------------------------- ### Sample Subscribe Packet Creation Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/subscribe.rs.html Creates a sample Subscribe packet with properties for testing. Includes subscription identifier, user properties, filter, and options. ```rust fn sample() -> Subscribe { let subscribe_properties = SubscribeProperties { id: Some(100), user_properties: vec![("test".to_owned(), "test".to_owned())], }; let mut filter = SubscribeFilter::new("hello".to_owned(), QoS::AtLeastOnce); filter .set_nolocal(true) .set_preserve_retain(true) .set_retain_forward_rule(RetainForwardRule::Never); Subscribe { pkid: 42, filters: vec![filter], properties: Some(subscribe_properties), } } ``` -------------------------------- ### Create a New MQTT Connect Packet Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v4/connect.rs.html Provides a constructor for the Connect struct, initializing it with default values for protocol (V4), keep-alive (10 seconds), and requiring only a client ID. Other fields like last will and login are set to None by default. ```rust pub fn new>(id: S) -> Connect { Connect { protocol: Protocol::V4, keep_alive: 10, client_id: id.into(), clean_session: true, last_will: None, login: None, } } ``` -------------------------------- ### Create a new MQTT Subscribe Filter Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/subscribe.rs.html Initializes a `SubscribeFilter` with a path and QoS level. Default values are set for `nolocal`, `preserve_retain`, and `retain_forward_rule`. ```rust pub fn new(path: String, qos: QoS) -> SubscribeFilter { SubscribeFilter { path, qos, nolocal: false, preserve_retain: false, retain_forward_rule: RetainForwardRule::OnEverySubscribe, } } ``` -------------------------------- ### Sample Bytes for MQTT CONNECT Packet Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v4/connect.rs.html Provides a byte vector representing a sample MQTT CONNECT packet. This is used for assertion in tests. ```rust fn sample_bytes() -> Vec { vec![ 0x10, 39, 0x00, 0x04, b'M', b'Q', b'T', b'T', 0x04, 0b1100_1110, // +username, +password, -will retain, will qos=1, +last_will, +clean_session 0x00, 0x0a, // 10 sec 0x00, 0x04, b't', b'e', b's', b't', // client_id 0x00, 0x02, b'/', b'a', // will topic = '/a' 0x00, 0x07, b'o', b'f', b'f', b'l', b'i', b'n', b'e', // will msg = 'offline' 0x00, 0x04, b'r', b'u', b's', b't', // username = 'rust' 0x00, 0x02, b'm', b'q', // password = 'mq' ] } ``` -------------------------------- ### Sample PubAck with Success Reason Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/puback.rs.html Defines a sample PubAck struct with the 'Success' reason code and no properties. This is used in unit tests. ```rust fn sample3() -> PubAck { PubAck { pkid: 42, reason: PubAckReason::Success, properties: None, } } ``` -------------------------------- ### Implement PubComp for MQTT QoS 1 Acknowledgement Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v4/pubcomp.rs.html Defines the PubComp struct for QoS 1 publish acknowledgements and implements methods for creating, calculating length, reading from bytes, and writing to a byte buffer. Requires the `bytes` crate for buffer manipulation. ```rust use super::*; use bytes::{Buf, BufMut, Bytes, BytesMut}; /// Acknowledgement to QoS1 publish #[derive(Debug, Clone, PartialEq)] pub struct PubComp { pub pkid: u16, } impl PubComp { pub fn new(pkid: u16) -> PubComp { PubComp { pkid } } fn len(&self) -> usize { let len = 2; // pkid len } pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result { let variable_header_index = fixed_header.fixed_header_len; bytes.advance(variable_header_index); let pkid = read_u16(&mut bytes)?; if fixed_header.remaining_len == 2 { return Ok(PubComp { pkid }); } if fixed_header.remaining_len < 4 { return Ok(PubComp { pkid }); } let puback = PubComp { pkid }; Ok(puback) } pub fn write(&self, buffer: &mut BytesMut) -> Result { let len = self.len(); buffer.put_u8(0x70); let count = write_remaining_length(buffer, len)?; buffer.put_u16(self.pkid); Ok(1 + count + len) } } ``` -------------------------------- ### Sample PubAck Bytes with Success Reason Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/puback.rs.html Provides the byte representation of a PubAck message with the 'Success' reason code and no properties. Used for testing. ```rust fn sample3_bytes() -> Vec { vec![0x40, 0x02, 0x00, 0x2a] } ``` -------------------------------- ### Sample PubAck with Properties for Testing Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/puback.rs.html Defines a sample PubAck struct with properties, including a reason string and user properties, for use in unit tests. This function is part of the test module. ```rust fn sample() -> PubAck { let properties = PubAckProperties { reason_string: Some("test".to_owned()), user_properties: vec![("test".to_owned(), "test".to_owned())], }; PubAck { pkid: 42, reason: PubAckReason::NoMatchingSubscribers, properties: Some(properties), } } ``` -------------------------------- ### Initialize LastWill Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/connect.rs.html Constructor for creating a new LastWill instance. ```rust impl LastWill { pub fn new( topic: impl Into, payload: impl Into>, qos: QoS, retain: bool, ) -> LastWill { LastWill { topic: topic.into(), message: Bytes::from(payload.into()), qos, retain, properties: None, } } ``` -------------------------------- ### Write Login Credentials to Bytes Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v4/connect.rs.html Serializes username and password into a byte buffer. Sets corresponding bits in connect flags if fields are not empty. Requires `write_mqtt_string`. ```rust fn write(&self, buffer: &mut BytesMut) -> u8 { let mut connect_flags = 0; if !self.username.is_empty() { connect_flags |= 0x80; write_mqtt_string(buffer, &self.username); } if !self.password.is_empty() { connect_flags |= 0x40; write_mqtt_string(buffer, &self.password); } connect_flags } ``` -------------------------------- ### Create New PubComp Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/pubcomp.rs.html Constructor for a new PubComp message, defaulting to Success reason and no properties. ```rust impl PubComp { pub fn new(pkid: u16) -> PubComp { PubComp { pkid, reason: PubCompReason::Success, properties: None, } } fn len(&self) -> usize { let mut len = 2 + 1; // pkid + reason // If there are no properties during success, sending reason code is optional if self.reason == PubCompReason::Success && self.properties.is_none() { return 2; } if let Some(properties) = &self.properties { let properties_len = properties.len(); let properties_len_len = len_len(properties_len); len += properties_len_len + properties_len; } len } // ... other methods } ``` -------------------------------- ### Sample UnsubAck Packet Creation Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/unsuback.rs.html Creates a sample UnsubAck packet with specific packet ID, reasons, and properties for testing purposes. ```rust fn sample() -> UnsubAck { let properties = UnsubAckProperties { reason_string: Some("test".to_owned()), user_properties: vec![("test".to_owned(), "test".to_owned())], }; UnsubAck { pkid: 10, reasons: vec![ UnsubAckReason::NotAuthorized, UnsubAckReason::TopicFilterInvalid, ], properties: Some(properties), } } ``` -------------------------------- ### Test MQTT Connect Packet Parsing Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v4/connect.rs.html A test case that constructs a byte stream representing an MQTT CONNECT packet with various options (username, password, will, etc.) and verifies its parsing. ```rust let mut stream = bytes::BytesMut::new(); let packetstream = &[ 0x10, 39, // packet type, flags and remaining len 0x00, 0x04, b'M', b'Q', b'T', b'T', 0x04, // variable header 0b1100_1110, // variable header. +username, +password, -will retain, will qos=1, +last_will, +clean_session 0x00, 0x0a, // variable header. keep alive = 10 sec 0x00, 0x04, b't', b'e', b's', b't', // payload. client_id 0x00, 0x02, b'/', b'a', // payload. will topic = '/a' 0x00, 0x07, b'o', b'f', b'f', b'l', b'i', b'n', b'e', // payload. variable header. will msg = 'offline' 0x00, 0x04, b'r', b'u', b'm', b'q', // payload. username = 'rumq' 0x00, 0x02, b'm', b'q', // payload. password = 'mq' 0xDE, 0xAD, 0xBE, 0xEF, // extra packets in the stream ]; stream.extend_from_slice(&packetstream[..]); let fixed_header = parse_fixed_header(stream.iter()).unwrap(); ``` -------------------------------- ### Create New Publish Packet Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v4/publish.rs.html Provides a constructor `new` for creating a Publish packet with a topic, QoS, and payload. Initializes control flags like `dup` and `retain` to false and `pkid` to 0. ```rust pub fn new, P: Into>>(topic: S, qos: QoS, payload: P) -> Publish { Publish { dup: false, qos, retain: false, pkid: 0, topic: topic.into(), payload: Bytes::from(payload.into()), } } ``` -------------------------------- ### Publish::from_bytes Constructor Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/publish.rs.html Creates a Publish packet directly from a topic, QoS, and Bytes payload. Initializes other fields to defaults. Useful when the payload is already in Bytes format. ```rust pub fn from_bytes>(topic: S, qos: QoS, payload: Bytes) -> Publish { Publish { dup: false, qos, retain: false, pkid: 0, topic: topic.into(), properties: None, payload, } } ``` -------------------------------- ### Implement PartialEq for SubAckProperties Source: https://docs.rs/mqttbytes/0.6.0/mqttbytes/v5/struct.SubAckProperties.html Enables comparison of two SubAckProperties instances for equality. ```rust fn eq(&self, other: &SubAckProperties) -> bool ``` -------------------------------- ### Sample PubAck Bytes with Properties Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/puback.rs.html Provides the byte representation of a sample PubAck message that includes properties. This is used to verify encoding and decoding. ```rust fn sample_bytes() -> Vec { vec![ 0x40, // payload type 0x18, // remaining length 0x00, 0x2a, // packet id 0x10, // reason 0x14, // properties len 0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // reason_string 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // user properties ] } ``` -------------------------------- ### Byte representation of Connect packet with properties Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/connect.rs.html Provides the exact byte sequence for the `sample3()` Connect packet. This is used as the expected output when testing the encoding of the Connect packet. ```rust fn sample3_bytes() -> Vec { vec![ 0x10, 0x6e, 0x00, 0x04, 0x4d, 0x51, 0x54, 0x54, 0x05, 0xc6, 0x00, 0x00, 0x2f, 0x11, 0x00, 0x00, 0x04, 0xd2, 0x21, 0x01, 0xb0, 0x27, 0x00, 0x00, 0x00, 0x64, 0x22, 0x01, 0xc8, 0x19, 0x01, 0x17, 0x01, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x15, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x16, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, 0x00, 0x09, 0x6d, 0x79, 0x2d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x00, 0x00, 0x0f, 0x6d, 0x79, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x00, 0x04, 0x64, 0x65, 0x61, 0x64, 0x00, 0x06, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x6f, 0x00, 0x07, 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x61, ] } ``` -------------------------------- ### Sample Byte Representation of Publish Packet Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/publish.rs.html Provides a byte vector representing a sample MQTT Publish packet, including its fixed header, remaining length, topic name, packet identifier, properties, and payload. This is used to verify the encoded output against expected byte sequences. ```rust fn sample_bytes() -> Vec { vec![ 0x34, // payload type 0x3e, // remaining len 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // topic name 0x00, 0x2a, // pkid 0x31, // properties len 0x01, 0x01, // payload format indicator 0x02, 0x00, 0x00, 0x10, 0xe1, // message_expiry_interval 0x23, 0x00, 0x64, // topic alias 0x08, 0x00, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, // response topic 0x09, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, // correlation_data 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // user properties 0x0b, 0x78, // subscription_identifier 0x0b, 0x79, // subscription_identifier 0x03, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // content_type 0x74, 0x65, 0x73, 0x74, // payload ] } ``` -------------------------------- ### Create Publish Packet from Bytes Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v4/publish.rs.html A constructor `from_bytes` to create a Publish packet directly from a topic, QoS, and a `Bytes` payload. Similar to `new`, it sets default values for `dup`, `retain`, and `pkid`. ```rust pub fn from_bytes>(topic: S, qos: QoS, payload: Bytes) -> Publish { Publish { dup: false, qos, retain: false, pkid: 0, topic: topic.into(), payload, } } ``` -------------------------------- ### MQTT Topic and Filter Operations Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/topic.rs.html Functions for checking wildcards, validating topics and filters, and matching topics against filters. ```rust use alloc::vec::Vec; /// Checks if a topic or topic filter has wildcards pub fn has_wildcards(s: &str) -> bool { s.contains('+') || s.contains('#') } /// Checks if a topic is valid pub fn valid_topic(topic: &str) -> bool { if topic.contains('+') { return false; } if topic.contains('#') { return false; } true } /// Checks if the filter is valid /// /// https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718106 pub fn valid_filter(filter: &str) -> bool { if filter.is_empty() { return false; } let hirerarchy = filter.split('/').collect::>(); if let Some((last, remaining)) = hirerarchy.split_last() { // # is not allowed in filer except as a last entry // invalid: sport/tennis#/player // invalid: sport/tennis/#/ranking for entry in remaining.iter() { if entry.contains('#') { return false; } } // only single '#" is allowed in last entry // invalid: sport/tennis# if last.len() != 1 && last.contains('#') { return false; } } true } /// Checks if topic matches a filter. topic and filter validation isn't done here. /// /// **NOTE**: 'topic' is a misnomer in the arg. this can also be used to match 2 wild subscriptions /// **NOTE**: make sure a topic is validated during a publish and filter is validated /// during a subscribe pub fn matches(topic: &str, filter: &str) -> bool { if !topic.is_empty() && topic[..1].contains('$') { return false; } let mut topics = topic.split('/'); let mut filters = filter.split('/'); for f in filters.by_ref() { // "#" being the last element is validated by the broker with 'valid_filter' if f == "#" { return true; } // filter still has remaining elements // filter = a/b/c/# should match topci = a/b/c // filter = a/b/c/d should not match topic = a/b/c let top = topics.next(); match top { Some(t) if t == "#" => return false, Some(_) if f == "+" => continue, Some(t) if f != t => return false, Some(_) => continue, None => return false, } } // topic has remaining elements and filter's last element isn't "#" if topics.next().is_some() { return false; } true } ``` ```rust #[cfg(test)] mod test { #[test] fn wildcards_are_detected_correctly() { assert!(!super::has_wildcards("a/b/c")); assert!(super::has_wildcards("a/+/c")); assert!(super::has_wildcards("a/b/#")); } #[test] fn topics_are_validated_correctly() { assert!(!super::valid_topic("+wrong")); assert!(!super::valid_topic("wro#ng")); assert!(!super::valid_topic("w/r/o/n/g+")); assert!(!super::valid_topic("wrong/#/path")); } #[test] fn filters_are_validated_correctly() { assert!(!super::valid_filter("wrong/#/filter")); assert!(!super::valid_filter("wrong/wr#ng/filter")); assert!(!super::valid_filter("wrong/filter#")); assert!(super::valid_filter("correct/filter/#")); } #[test] fn zero_len_subscriptions_are_not_allowed() { assert!(!super::valid_filter("")); } #[test] fn dollar_subscriptions_doesnt_match_dollar_topic() { assert!(super::matches("sy$tem/metrics", "sy$tem/+")); assert!(!super::matches("$system/metrics", "$system/+")); assert!(!super::matches("$system/metrics", "+/+")); } #[test] fn topics_match_with_filters_as_expected() { let topic = "a/b/c"; let filter = "a/b/c"; assert!(super::matches(topic, filter)); let topic = "a/b/c"; let filter = "d/b/c"; assert!(!super::matches(topic, filter)); let topic = "a/b/c"; let filter = "a/b/e"; assert!(!super::matches(topic, filter)); let topic = "a/b/c"; let filter = "a/b/c/d"; assert!(!super::matches(topic, filter)); let topic = "a/b/c"; let filter = "#"; assert!(super::matches(topic, filter)); let topic = "a/b/c"; let filter = "a/b/c/#"; assert!(super::matches(topic, filter)); let topic = "a/b/c/d"; let filter = "a/b/c"; assert!(!super::matches(topic, filter)); let topic = "a/b/c/d"; let filter = "a/b/c/#"; assert!(super::matches(topic, filter)); } } ``` -------------------------------- ### Sample Unsubscribe Packet Bytes (No Properties) Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/unsubscribe.rs.html Provides the byte representation of a sample MQTT Unsubscribe packet with a single filter and no properties. Used for testing parsing and encoding. ```rust fn sample2_bytes() -> Vec { vec![ 0xa2, 0x0a, 0x00, 0x0a, 0x00, 0x00, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, ] } ``` -------------------------------- ### Implement PartialEq for PubRecProperties Source: https://docs.rs/mqttbytes/0.6.0/mqttbytes/v5/struct.PubRecProperties.html Allows for the comparison of two PubRecProperties instances for equality. ```rust fn eq(&self, other: &PubRecProperties) -> bool ``` ```rust fn ne(&self, other: &Rhs) -> bool ``` -------------------------------- ### PubRec::new Constructor Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/pubrec.rs.html Creates a new PubRec packet with a given packet identifier and defaults to Success reason code with no properties. Use this for basic acknowledgments. ```rust pub fn new(pkid: u16) -> PubRec { PubRec { pkid, reason: PubRecReason::Success, properties: None, } } ``` -------------------------------- ### Parse MQTT Publish Packet Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v4/publish.rs.html Demonstrates reading a raw byte stream into a Publish packet structure. ```rust let stream = &[ 0b0011_0000, 7, // packet type, flags and remaining len 0x00, 0x03, b'a', b'/', b'b', // variable header. topic name = 'a/b' 0x01, 0x02, // payload 0xDE, 0xAD, 0xBE, 0xEF, // extra packets in the stream ]; let mut stream = BytesMut::from(&stream[..]); let fixed_header = parse_fixed_header(stream.iter()).unwrap(); let publish_bytes = stream.split_to(fixed_header.frame_length()).freeze(); let packet = Publish::read(fixed_header, publish_bytes).unwrap(); assert_eq!( packet, Publish { dup: false, qos: QoS::AtMostOnce, retain: false, topic: "a/b".to_owned(), pkid: 0, payload: Bytes::from(&[0x01, 0x02][..]), } ); ``` -------------------------------- ### Sample PubAck without Properties for Testing Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/puback.rs.html Defines a sample PubAck struct without any properties, used for testing scenarios where properties are absent. This is part of the test suite. ```rust fn sample2() -> PubAck { PubAck { pkid: 42, reason: PubAckReason::NoMatchingSubscribers, properties: None, } } ``` -------------------------------- ### Publish::new Constructor Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/publish.rs.html Creates a new Publish packet with default values for dup and retain flags. Requires topic, QoS, and payload. Topic and payload are converted to their respective types. ```rust pub fn new, P: Into>>(topic: S, qos: QoS, payload: P) -> Publish { Publish { dup: false, qos, retain: false, pkid: 0, topic: topic.into(), properties: None, payload: Bytes::from(payload.into()), } } ``` -------------------------------- ### Test MQTT Connection Acknowledgement Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/connack.rs.html Provides sample data structures and byte representations for testing ConnAck packet handling. ```rust fn sample() -> ConnAck { let properties = ConnAckProperties { session_expiry_interval: Some(1234), receive_max: Some(432), max_qos: Some(2), retain_available: Some(1), max_packet_size: Some(100), assigned_client_identifier: Some("test".to_owned()), topic_alias_max: Some(456), reason_string: Some("test".to_owned()), user_properties: vec![("test".to_owned(), "test".to_owned())], wildcard_subscription_available: Some(1), subscription_identifiers_available: Some(1), shared_subscription_available: Some(0), server_keep_alive: Some(1234), response_information: Some("test".to_owned()), server_reference: Some("test".to_owned()), authentication_method: Some("test".to_owned()), authentication_data: Some(Bytes::from(vec![1, 2, 3, 4])), }; ConnAck { session_present: false, code: ConnectReturnCode::Success, properties: Some(properties), } } fn sample_bytes() -> Vec { vec![ 0x20, // Packet type 0x57, // Remaining length 0x00, 0x00, // Session, code 0x54, // Properties length 0x11, 0x00, 0x00, 0x04, 0xd2, // Session expiry interval 0x21, 0x01, 0xb0, // Receive maximum 0x24, 0x02, // Maximum qos 0x25, 0x01, // Retain available 0x27, 0x00, 0x00, 0x00, 0x64, // Maximum packet size 0x12, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // Assigned client identifier 0x22, 0x01, 0xc8, // Topic alias max 0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // Reason string 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // user properties 0x28, 0x01, // wildcard_subscription_available ``` -------------------------------- ### Connect Struct Definition Source: https://docs.rs/mqttbytes/0.6.0/mqttbytes/v5/struct.Connect.html The structure definition for the MQTT Connect packet. ```rust pub struct Connect { pub protocol: Protocol, pub keep_alive: u16, pub client_id: String, pub clean_session: bool, pub last_will: Option, pub login: Option, pub properties: Option, } ``` -------------------------------- ### Create New LastWill Instance Source: https://docs.rs/mqttbytes/0.6.0/mqttbytes/v4/struct.LastWill.html Constructor for creating a new LastWill instance. Accepts topic, payload, QoS, and retain flag. ```rust pub fn new( topic: impl Into, payload: impl Into>, qos: QoS, retain: bool, ) -> LastWill ``` -------------------------------- ### Define Connect struct and methods Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/connect.rs.html The Connect struct represents the MQTT connection packet. It includes methods for creating a new instance, setting login credentials, calculating packet length, and reading/writing the packet to a buffer. ```rust 1use super::*; 2use alloc::string::String; 3use alloc::vec::Vec; 4use bytes::{Buf, Bytes}; 5 6/// Connection packet initiated by the client 7#[derive(Debug, Clone, PartialEq)] 8pub struct Connect { 9 /// Mqtt protocol version 10 pub protocol: Protocol, 11 /// Mqtt keep alive time 12 pub keep_alive: u16, 13 /// Client Id 14 pub client_id: String, 15 /// Clean session. Asks the broker to clear previous state 16 pub clean_session: bool, 17 /// Will that broker needs to publish when the client disconnects 18 pub last_will: Option, 19 /// Login credentials 20 pub login: Option, 21 /// Properties 22 pub properties: Option, 23} 24 25impl Connect { 26 pub fn new>(id: S) -> Connect { 27 Connect { 28 protocol: Protocol::V5, 29 keep_alive: 10, 30 properties: None, 31 client_id: id.into(), 32 clean_session: true, 33 last_will: None, 34 login: None, 35 } 36 } 37 38 pub fn set_login>(&mut self, u: S, p: S) -> &mut Connect { 39 let login = Login { 40 username: u.into(), 41 password: p.into(), 42 }; 43 44 self.login = Some(login); 45 self 46 } 47 48 pub fn len(&self) -> usize { 49 let mut len = 2 + "MQTT".len() // protocol name 50 + 1 // protocol version 51 + 1 // connect flags 52 + 2; // keep alive 53 54 match &self.properties { 55 Some(properties) => { 56 let properties_len = properties.len(); 57 let properties_len_len = len_len(properties_len); 58 len += properties_len_len + properties_len; 59 } 60 None => { 61 // just 1 byte representing 0 len 62 len += 1; 63 } 64 } 65 66 len += 2 + self.client_id.len(); 67 68 // last will len 69 if let Some(last_will) = &self.last_will { 70 len += last_will.len(); 71 } 72 73 // username and password len 74 if let Some(login) = &self.login { 75 len += login.len(); 76 } 77 78 len 79 } 80 81 pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result { 82 let variable_header_index = fixed_header.fixed_header_len; 83 bytes.advance(variable_header_index); 84 85 // Variable header 86 let protocol_name = read_mqtt_string(&mut bytes)?; 87 let protocol_level = read_u8(&mut bytes)?; 88 if protocol_name != "MQTT" { 89 return Err(Error::InvalidProtocol); 90 } 91 92 let protocol = match protocol_level { 93 4 => Protocol::V4, 94 5 => Protocol::V5, 95 num => return Err(Error::InvalidProtocolLevel(num)), 96 }; 97 98 let connect_flags = read_u8(&mut bytes)?; 99 let clean_session = (connect_flags & 0b10) != 0; 100 let keep_alive = read_u16(&mut bytes)?; 101 102 // Properties in variable header 103 let properties = match protocol { 104 Protocol::V5 => ConnectProperties::read(&mut bytes)?, 105 Protocol::V4 => None, 106 }; 107 108 let client_id = read_mqtt_string(&mut bytes)?; 109 let last_will = LastWill::read(connect_flags, &mut bytes)?; 110 let login = Login::read(connect_flags, &mut bytes)?; 111 112 let connect = Connect { 113 protocol, 114 keep_alive, 115 properties, 116 client_id, 117 clean_session, 118 last_will, 119 login, 120 }; 121 122 Ok(connect) 123 } 124 125 pub fn write(&self, buffer: &mut BytesMut) -> Result { 126 let len = self.len(); 127 buffer.put_u8(0b0001_0000); 128 let count = write_remaining_length(buffer, len)?; 129 write_mqtt_string(buffer, "MQTT"); 130 131 match self.protocol { 132 Protocol::V4 => buffer.put_u8(0x04), 133 Protocol::V5 => buffer.put_u8(0x05), 134 } 135 136 let flags_index = 1 + count + 2 + 4 + 1; 137 138 let mut connect_flags = 0; 139 if self.clean_session { 140 connect_flags |= 0x02; 141 } 142 143 buffer.put_u8(connect_flags); 144 buffer.put_u16(self.keep_alive); 145 146 match &self.properties { 147 Some(properties) => properties.write(buffer)?, 148 None => { 149 write_remaining_length(buffer, 0)?; 150 } 151 }; 152 153 write_mqtt_string(buffer, &self.client_id); 154 155 if let Some(last_will) = &self.last_will { 156 connect_flags |= last_will.write(buffer)?; 157 } ``` -------------------------------- ### Set MQTT Subscribe Filter Options Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/subscribe.rs.html Provides builder-style methods to configure `nolocal`, `preserve_retain`, and `retain_forward_rule` for a `SubscribeFilter`. ```rust pub fn set_nolocal(&mut self, flag: bool) -> &mut Self { self.nolocal = flag; self } ``` ```rust pub fn set_preserve_retain(&mut self, flag: bool) -> &mut Self { self.preserve_retain = flag; self } ``` ```rust pub fn set_retain_forward_rule(&mut self, rule: RetainForwardRule) -> &mut Self { self.retain_forward_rule = rule; self } ``` -------------------------------- ### Sample PUBREC Data Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v5/pubrec.rs.html Provides a sample `PubRec` struct instance for testing purposes. Includes a packet identifier, reason, and optional properties like reason string and user properties. ```rust fn sample() -> PubRec { let properties = PubRecProperties { reason_string: Some("test".to_owned()), user_properties: vec![("test".to_owned(), "test".to_owned())], }; PubRec { pkid: 42, reason: PubRecReason::NoMatchingSubscribers, properties: Some(properties), } } ``` -------------------------------- ### Create New Login Source: https://docs.rs/mqttbytes/0.6.0/src/mqttbytes/v4/connect.rs.html Constructor for the Login struct, taking username and password as generic types that can be converted into Strings. ```rust pub fn new>(u: S, p: S) -> Login { Login { username: u.into(), password: p.into(), } } ``` -------------------------------- ### Set Login Credentials for Connect Source: https://docs.rs/mqttbytes/0.6.0/mqttbytes/v4/struct.Connect.html Configures username and password for the `Connect` packet. This method returns a mutable reference to the `Connect` struct for chaining. ```rust pub fn set_login>(&mut self, u: S, p: S) -> &mut Connect ```