diff --git a/src/mqtt/packet/mqtt_binary.rs b/src/mqtt/packet/mqtt_binary.rs index 8aec68c..db93196 100644 --- a/src/mqtt/packet/mqtt_binary.rs +++ b/src/mqtt/packet/mqtt_binary.rs @@ -285,6 +285,40 @@ impl TryFrom<&str> for MqttBinary { } } +/// Implementation of `TryFrom<&[u8; N]>` for `MqttBinary` +/// +/// Converts a fixed-size array reference to `MqttBinary`. This allows array literals +/// like `b"data"` to be used with generic code that accepts `TryInto`. +impl TryFrom<&[u8; N]> for MqttBinary { + type Error = MqttError; + + fn try_from(arr: &[u8; N]) -> Result { + Self::new(&arr[..]) + } +} + +/// Implementation of `TryFrom>` for `MqttBinary` +/// +/// Converts an owned `Vec` to `MqttBinary`. +impl TryFrom> for MqttBinary { + type Error = MqttError; + + fn try_from(v: Vec) -> Result { + Self::new(&v) + } +} + +/// Implementation of `TryFrom<&Vec>` for `MqttBinary` +/// +/// Converts a `Vec` reference to `MqttBinary`. +impl TryFrom<&Vec> for MqttBinary { + type Error = MqttError; + + fn try_from(v: &Vec) -> Result { + Self::new(v.as_slice()) + } +} + /// Implementation of `Default` for `MqttBinary` impl Default for MqttBinary { fn default() -> Self { diff --git a/src/mqtt/packet/mqtt_string.rs b/src/mqtt/packet/mqtt_string.rs index 5b1627a..6bfaa4e 100644 --- a/src/mqtt/packet/mqtt_string.rs +++ b/src/mqtt/packet/mqtt_string.rs @@ -645,7 +645,7 @@ impl core::hash::Hash for MqttString { /// The internal buffer contains only the 2-byte length prefix (0x00, 0x00). impl Default for MqttString { fn default() -> Self { - MqttString::new("").unwrap() + Self::new("").unwrap() } } @@ -665,7 +665,7 @@ impl TryFrom<&str> for MqttString { type Error = MqttError; fn try_from(s: &str) -> Result { - MqttString::new(s) + Self::new(s) } } @@ -677,7 +677,18 @@ impl TryFrom for MqttString { type Error = MqttError; fn try_from(s: String) -> Result { - MqttString::new(s) + Self::new(s) + } +} + +/// Implementation of `TryFrom<&String>` for `MqttString` +/// +/// Converts a `String` reference to `MqttString`. +impl TryFrom<&String> for MqttString { + type Error = MqttError; + + fn try_from(s: &String) -> Result { + Self::new(s.as_str()) } } diff --git a/src/mqtt/packet/property.rs b/src/mqtt/packet/property.rs index cc0b873..0ed0945 100644 --- a/src/mqtt/packet/property.rs +++ b/src/mqtt/packet/property.rs @@ -27,7 +27,7 @@ use crate::mqtt::packet::DecodeResult; use crate::mqtt::packet::VariableByteInteger; use crate::mqtt::result_code::MqttError; use alloc::{string::String, vec::Vec}; -use core::convert::TryFrom; +use core::convert::{TryFrom, TryInto}; use core::fmt; use num_enum::TryFromPrimitive; use serde::ser::SerializeStruct; @@ -372,9 +372,9 @@ macro_rules! mqtt_property_binary { /// ``` pub fn new(v: T) -> Result where - T: AsRef<[u8]>, + T: TryInto, { - let binary = MqttBinary::new(v)?; + let binary = v.try_into()?; Ok(Self { id_bytes: [$id as u8], @@ -553,9 +553,9 @@ macro_rules! mqtt_property_string { /// ``` pub fn new(s: T) -> Result where - T: AsRef, + T: TryInto, { - let value = MqttString::new(s)?; + let value = s.try_into()?; Ok(Self { id_bytes: [$id as u8], @@ -716,11 +716,11 @@ macro_rules! mqtt_property_string_pair { /// ``` pub fn new(key: K, val: V) -> Result where - K: AsRef, - V: AsRef, + K: TryInto, + V: TryInto, { - let key_mqtt = MqttString::new(key)?; - let val_mqtt = MqttString::new(val)?; + let key_mqtt = key.try_into()?; + let val_mqtt = val.try_into()?; Ok(Self { id_bytes: [$id as u8], diff --git a/src/mqtt/packet/sub_entry.rs b/src/mqtt/packet/sub_entry.rs index 6cc59dd..5c25e9c 100644 --- a/src/mqtt/packet/sub_entry.rs +++ b/src/mqtt/packet/sub_entry.rs @@ -26,6 +26,7 @@ use crate::mqtt::packet::RetainHandling; use crate::mqtt::result_code::MqttError; use alloc::string::ToString; use alloc::{string::String, vec::Vec}; +use core::convert::TryInto; use core::fmt; use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; @@ -564,8 +565,11 @@ impl SubEntry { /// let opts = mqtt::packet::SubOpts::new().set_qos(mqtt::packet::Qos::AtLeastOnce); /// let entry = mqtt::packet::SubEntry::new("home/+/status", opts).unwrap(); /// ``` - pub fn new(topic_filter: impl AsRef, sub_opts: SubOpts) -> Result { - let topic_filter = MqttString::new(topic_filter)?; + pub fn new(topic_filter: T, sub_opts: SubOpts) -> Result + where + T: TryInto, + { + let topic_filter = topic_filter.try_into()?; Ok(Self { topic_filter, sub_opts, diff --git a/src/mqtt/packet/v3_1_1/connect.rs b/src/mqtt/packet/v3_1_1/connect.rs index 0610f58..3bb1dc2 100644 --- a/src/mqtt/packet/v3_1_1/connect.rs +++ b/src/mqtt/packet/v3_1_1/connect.rs @@ -40,6 +40,7 @@ use crate::mqtt::packet::variable_byte_integer::VariableByteInteger; use crate::mqtt::packet::GenericPacketDisplay; use crate::mqtt::packet::GenericPacketTrait; use crate::mqtt::result_code::MqttError; +use core::convert::TryInto; /// MQTT v3.1.1 CONNECT packet representation /// @@ -679,8 +680,11 @@ impl ConnectBuilder { /// .client_id("device-001") /// .unwrap(); /// ``` - pub fn client_id(mut self, id: impl AsRef) -> Result { - let mqtt_str = MqttString::new(id.as_ref())?; + pub fn client_id(mut self, id: T) -> Result + where + T: TryInto, + { + let mqtt_str = id.try_into()?; self.client_id_buf = Some(mqtt_str); Ok(self) } @@ -766,15 +770,19 @@ impl ConnectBuilder { /// .build() /// .unwrap(); /// ``` - pub fn will_message( + pub fn will_message( mut self, - topic: impl AsRef, - payload: impl AsRef<[u8]>, + topic: T, + payload: B, qos: Qos, retain: bool, - ) -> Result { - let will_topic = MqttString::new(topic.as_ref())?; - let will_payload = MqttBinary::new(payload.as_ref().to_vec())?; + ) -> Result + where + T: TryInto, + B: TryInto, + { + let will_topic = topic.try_into()?; + let will_payload = payload.try_into()?; self.will_topic_buf = Some(will_topic); self.will_payload_buf = Some(will_payload); @@ -815,8 +823,11 @@ impl ConnectBuilder { /// .build() /// .unwrap(); /// ``` - pub fn user_name(mut self, name: impl AsRef) -> Result { - let mqtt_str = MqttString::new(name.as_ref())?; + pub fn user_name(mut self, name: T) -> Result + where + T: TryInto, + { + let mqtt_str = name.try_into()?; self.user_name_buf = Some(mqtt_str); let mut flags = self.connect_flags_buf.unwrap_or([0b0000_0010])[0]; @@ -862,8 +873,11 @@ impl ConnectBuilder { /// .build() /// .unwrap(); /// ``` - pub fn password(mut self, pwd: impl AsRef<[u8]>) -> Result { - let mqtt_bin = MqttBinary::new(pwd.as_ref().to_vec())?; + pub fn password(mut self, pwd: B) -> Result + where + B: TryInto, + { + let mqtt_bin = pwd.try_into()?; self.password_buf = Some(mqtt_bin); let mut flags = self.connect_flags_buf.unwrap_or([0b0000_0010])[0]; diff --git a/src/mqtt/packet/v3_1_1/publish.rs b/src/mqtt/packet/v3_1_1/publish.rs index 3d28550..e8075aa 100644 --- a/src/mqtt/packet/v3_1_1/publish.rs +++ b/src/mqtt/packet/v3_1_1/publish.rs @@ -690,9 +690,16 @@ where /// is published. Topic names must be valid UTF-8 strings and cannot contain /// wildcard characters (+ or #) which are reserved for subscription filters. /// + /// This method accepts both string types (&str, String) and pre-constructed + /// `MqttString` instances. When passing a pre-constructed `MqttString`, no + /// additional heap allocation occurs, making it efficient for cross-thread + /// message passing scenarios. + /// /// # Parameters /// - /// * `topic` - The topic name as a string reference + /// * `topic` - The topic name. Can be: + /// - `&str` or `String` (will be converted to `MqttString`) + /// - `MqttString` (passed by value without additional allocation) /// /// # Returns /// @@ -711,17 +718,27 @@ where /// ```ignore /// use mqtt_protocol_core::mqtt; /// + /// // From &str (backward compatible) /// let builder = mqtt::packet::v3_1_1::Publish::builder() /// .topic_name("sensors/temperature/room1") /// .unwrap(); /// + /// // From pre-constructed MqttString (no additional allocation) + /// let topic = mqtt::packet::MqttString::new("sensors/temperature/room1").unwrap(); + /// let builder = mqtt::packet::v3_1_1::Publish::builder() + /// .topic_name(topic) + /// .unwrap(); + /// /// // This would fail due to wildcard /// // let invalid = mqtt::packet::v3_1_1::Publish::builder() /// // .topic_name("sensors/+/temperature") /// // .unwrap(); /// ``` - pub fn topic_name>(mut self, topic: T) -> Result { - let mqtt_str = MqttString::new(topic)?; + pub fn topic_name(mut self, topic: T) -> Result + where + T: TryInto, + { + let mqtt_str = topic.try_into()?; if mqtt_str.as_str().contains('#') || mqtt_str.as_str().contains('+') { return Err(MqttError::MalformedPacket); } diff --git a/src/mqtt/packet/v5_0/connect.rs b/src/mqtt/packet/v5_0/connect.rs index 7c694cb..7a27792 100644 --- a/src/mqtt/packet/v5_0/connect.rs +++ b/src/mqtt/packet/v5_0/connect.rs @@ -37,6 +37,7 @@ use crate::mqtt::packet::mqtt_string::MqttString; use crate::mqtt::packet::packet_type::{FixedHeader, PacketType}; use crate::mqtt::packet::GenericPacketDisplay; use crate::mqtt::packet::GenericPacketTrait; +use core::convert::TryInto; use crate::mqtt::packet::property::PropertiesToContinuousBuffer; use crate::mqtt::packet::qos::Qos; @@ -734,8 +735,11 @@ impl ConnectBuilder { /// .build() /// .unwrap(); /// ``` - pub fn client_id(mut self, id: impl AsRef) -> Result { - let mqtt_str = MqttString::new(id.as_ref())?; + pub fn client_id(mut self, id: T) -> Result + where + T: TryInto, + { + let mqtt_str = id.try_into()?; self.client_id_buf = Some(mqtt_str); Ok(self) } @@ -814,15 +818,19 @@ impl ConnectBuilder { /// .build() /// .unwrap(); /// ``` - pub fn will_message( + pub fn will_message( mut self, - topic: impl AsRef, - payload: impl AsRef<[u8]>, + topic: T, + payload: B, qos: Qos, retain: bool, - ) -> Result { - let will_topic = MqttString::new(topic.as_ref())?; - let will_payload = MqttBinary::new(payload.as_ref().to_vec())?; + ) -> Result + where + T: TryInto, + B: TryInto, + { + let will_topic = topic.try_into()?; + let will_payload = payload.try_into()?; self.will_topic_buf = Some(will_topic); self.will_payload_buf = Some(will_payload); @@ -863,8 +871,11 @@ impl ConnectBuilder { /// .build() /// .unwrap(); /// ``` - pub fn user_name(mut self, name: impl AsRef) -> Result { - let mqtt_str = MqttString::new(name.as_ref())?; + pub fn user_name(mut self, name: T) -> Result + where + T: TryInto, + { + let mqtt_str = name.try_into()?; self.user_name_buf = Some(mqtt_str); let mut flags = self.connect_flags_buf.unwrap_or([0b0000_0010])[0]; @@ -900,8 +911,11 @@ impl ConnectBuilder { /// .build() /// .unwrap(); /// ``` - pub fn password(mut self, pwd: impl AsRef<[u8]>) -> Result { - let mqtt_bin = MqttBinary::new(pwd.as_ref().to_vec())?; + pub fn password(mut self, pwd: B) -> Result + where + B: TryInto, + { + let mqtt_bin = pwd.try_into()?; self.password_buf = Some(mqtt_bin); let mut flags = self.connect_flags_buf.unwrap_or([0b0000_0010])[0]; diff --git a/src/mqtt/packet/v5_0/publish.rs b/src/mqtt/packet/v5_0/publish.rs index 7223f68..8c7b58f 100644 --- a/src/mqtt/packet/v5_0/publish.rs +++ b/src/mqtt/packet/v5_0/publish.rs @@ -1004,9 +1004,16 @@ where /// is published. Topic names cannot contain wildcard characters (+ or #) /// in PUBLISH packets, as these are only allowed in SUBSCRIBE packets. /// + /// This method accepts both string types (&str, String) and pre-constructed + /// `MqttString` instances. When passing a pre-constructed `MqttString`, no + /// additional heap allocation occurs, making it efficient for cross-thread + /// message passing scenarios. + /// /// # Parameters /// - /// - `topic`: The topic name as any type that can be referenced as a string + /// - `topic`: The topic name. Can be: + /// - `&str` or `String` (will be converted to `MqttString`) + /// - `MqttString` (passed by value without additional allocation) /// /// # Returns /// @@ -1023,12 +1030,22 @@ where /// ```ignore /// use mqtt_protocol_core::mqtt; /// + /// // From &str (backward compatible) /// let builder = mqtt::packet::v5_0::Publish::builder() /// .topic_name("sensors/temperature/room1") /// .unwrap(); + /// + /// // From pre-constructed MqttString (no additional allocation) + /// let topic = mqtt::packet::MqttString::new("sensors/temperature/room1").unwrap(); + /// let builder = mqtt::packet::v5_0::Publish::builder() + /// .topic_name(topic) + /// .unwrap(); /// ``` - pub fn topic_name>(mut self, topic: T) -> Result { - let mqtt_str = MqttString::new(topic)?; + pub fn topic_name(mut self, topic: T) -> Result + where + T: TryInto, + { + let mqtt_str = topic.try_into()?; if mqtt_str.as_str().contains('#') || mqtt_str.as_str().contains('+') { return Err(MqttError::MalformedPacket); }