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}