Skip to main content

rust_mqtt/types/
binary.rs

1use const_fn::const_fn;
2
3use crate::{
4    bytes::Bytes,
5    fmt::const_debug_assert,
6    types::{MqttString, TooLargeToEncode},
7};
8
9/// Arbitrary binary data with a length less than or equal to [`MqttBinary::MAX_LENGTH`] ([`u16::MAX`]).
10/// Exceeding this size ultimately leads to malformed packets.
11///
12/// # Examples
13///
14/// ```rust
15/// use rust_mqtt::Bytes;
16/// use rust_mqtt::types::{MqttBinary, MqttString, TooLargeToEncode};
17///
18/// let slice = [0x00; MqttBinary::MAX_LENGTH];
19/// let too_long = [0x00; MqttBinary::MAX_LENGTH + 1];
20///
21/// let b = MqttBinary::from_slice(&slice)?;
22/// assert_eq!(b.as_bytes(), &slice);
23/// assert!(MqttBinary::from_slice(&too_long).is_err());
24///
25/// let b = MqttBinary::from_bytes(Bytes::Borrowed(&slice))?;
26/// assert_eq!(b.as_bytes(), &slice);
27/// assert!(MqttBinary::from_bytes(Bytes::Borrowed(&too_long)).is_err());
28///
29/// let from_slice_unchecked = MqttBinary::from_slice_unchecked(&slice);
30/// assert_eq!(from_slice_unchecked.as_bytes(), &slice);
31///
32/// let from_bytes_unchecked = MqttBinary::from_bytes_unchecked(Bytes::Borrowed(&slice));
33/// assert_eq!(from_bytes_unchecked.as_bytes(), &slice);
34///
35/// # Ok::<(), TooLargeToEncode>(())
36/// ```
37#[derive(Default, Clone, PartialEq, Eq)]
38pub struct MqttBinary<'b>(pub(crate) Bytes<'b>);
39
40impl core::fmt::Debug for MqttBinary<'_> {
41    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
42        f.debug_tuple("MqttBinary").field(&self.as_ref()).finish()
43    }
44}
45
46#[cfg(feature = "defmt")]
47impl<'a> defmt::Format for MqttBinary<'a> {
48    fn format(&self, fmt: defmt::Formatter) {
49        defmt::write!(fmt, "MqttBinary({:?})", self.as_ref());
50    }
51}
52
53impl<'b> TryFrom<&'b [u8]> for MqttBinary<'b> {
54    type Error = TooLargeToEncode;
55
56    fn try_from(value: &'b [u8]) -> Result<Self, Self::Error> {
57        Self::from_slice(value)
58    }
59}
60impl<'b> TryFrom<&'b str> for MqttBinary<'b> {
61    type Error = TooLargeToEncode;
62
63    fn try_from(value: &'b str) -> Result<Self, Self::Error> {
64        Self::from_slice(value.as_bytes())
65    }
66}
67impl<'b> From<MqttString<'b>> for MqttBinary<'b> {
68    fn from(value: MqttString<'b>) -> Self {
69        Self(value.0.0)
70    }
71}
72
73impl AsRef<[u8]> for MqttBinary<'_> {
74    fn as_ref(&self) -> &[u8] {
75        self.as_bytes()
76    }
77}
78
79impl<'b> MqttBinary<'b> {
80    /// The maximum length of binary data so that it can be encoded. This value is limited by the 2-byte length field.
81    pub const MAX_LENGTH: usize = u16::MAX as usize;
82
83    /// Converts [`Bytes`] into [`MqttBinary`] by checking for the max length of
84    /// [`MqttBinary::MAX_LENGTH`].
85    ///
86    /// # Errors
87    ///
88    /// Returns [`TooLargeToEncode`] if `bytes`' length exceeds [`MqttBinary::MAX_LENGTH`].
89    #[const_fn(cfg(not(feature = "alloc")))]
90    pub const fn from_bytes(bytes: Bytes<'b>) -> Result<Self, TooLargeToEncode> {
91        match bytes.len() {
92            ..=Self::MAX_LENGTH => Ok(Self(bytes)),
93            _ => Err(TooLargeToEncode),
94        }
95    }
96
97    /// Converts a slice into [`MqttBinary`] by cloning the reference and checking for the max
98    /// length of [`MqttBinary::MAX_LENGTH`].
99    ///
100    /// # Errors
101    ///
102    /// Returns [`TooLargeToEncode`] if `slice`'s length exceeds [`MqttBinary::MAX_LENGTH`].
103    pub const fn from_slice(slice: &'b [u8]) -> Result<Self, TooLargeToEncode> {
104        match slice.len() {
105            ..=Self::MAX_LENGTH => Ok(Self(Bytes::Borrowed(slice))),
106            _ => Err(TooLargeToEncode),
107        }
108    }
109
110    /// Converts [`Bytes`] into [`MqttBinary`] without checking for the max length of
111    /// [`MqttBinary::MAX_LENGTH`].
112    ///
113    /// # Invariants
114    ///
115    /// The length of the slice parameter in bytes is less than or equal to
116    /// [`MqttBinary::MAX_LENGTH`].
117    ///
118    /// # Panics
119    ///
120    /// In debug builds, this function will panic if the bytes' length is greater than
121    /// [`MqttBinary::MAX_LENGTH`].
122    #[must_use]
123    pub const fn from_bytes_unchecked(bytes: Bytes<'b>) -> Self {
124        const_debug_assert!(
125            bytes.len() <= Self::MAX_LENGTH,
126            "the slice's length exceeds MAX_LENGTH"
127        );
128
129        Self(bytes)
130    }
131
132    /// Converts a slice into [`MqttBinary`] without checking for the max length of
133    /// [`MqttBinary::MAX_LENGTH`].
134    ///
135    /// # Invariants
136    ///
137    /// The length of the slice parameter in bytes is less than or equal to
138    /// [`MqttBinary::MAX_LENGTH`].
139    ///
140    /// # Panics
141    ///
142    /// In debug builds, this function will panic if the slice's length is greater than
143    /// [`MqttBinary::MAX_LENGTH`].
144    #[must_use]
145    pub const fn from_slice_unchecked(slice: &'b [u8]) -> Self {
146        const_debug_assert!(
147            slice.len() <= Self::MAX_LENGTH,
148            "the slice's length exceeds MAX_LENGTH"
149        );
150
151        Self(Bytes::Borrowed(slice))
152    }
153
154    /// Returns the length of the underlying data.
155    #[inline]
156    #[must_use]
157    pub const fn len(&self) -> u16 {
158        self.0.len() as u16
159    }
160
161    /// Returns whether the underlying data is empty.
162    #[inline]
163    #[must_use]
164    pub const fn is_empty(&self) -> bool {
165        self.0.is_empty()
166    }
167
168    /// Returns the underlying bytes as `&[u8]`
169    #[inline]
170    #[must_use]
171    pub const fn as_bytes(&self) -> &[u8] {
172        self.0.as_bytes()
173    }
174
175    /// Delegates to [`Bytes::as_borrowed`].
176    #[inline]
177    #[must_use]
178    pub const fn as_borrowed(&'b self) -> Self {
179        Self(self.0.as_borrowed())
180    }
181}