mqute_codec/protocol/common/
payload.rs

1//! # Codes and Topic Filters
2//!
3//! This module provides utilities for handling MQTT protocol-specific data structures,
4//! specifically `Codes` and `TopicFilters`. These structures are used to encode and decode
5//! MQTT protocol data, such as return codes and topic filters, which are essential for
6//! MQTT communication.
7
8use crate::Error;
9use crate::codec::util::{decode_string, encode_string};
10use crate::protocol::util;
11use bytes::{Buf, BufMut, Bytes, BytesMut};
12use std::borrow::Borrow;
13use std::ops::{Index, IndexMut};
14
15/// The `Codes` module provides a generic structure to handle a collection of MQTT return codes.
16/// These codes are used in various MQTT control packets, such as ConnAck, SubAck, and UnsubAck.
17///
18/// # Example
19///
20/// ```rust
21/// use mqute_codec::protocol::{Codes, QoS};
22/// use mqute_codec::protocol::v4::ReturnCode;
23///
24/// let values = vec![ReturnCode::Failure, ReturnCode::Success(QoS::AtLeastOnce)];
25/// let codes: Codes<ReturnCode> = Codes::new(values);
26/// assert_eq!(codes.len(), 2);
27/// ```
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct Codes<T>(Vec<T>);
30
31#[allow(clippy::len_without_is_empty)]
32impl<T> Codes<T>
33where
34    T: TryFrom<u8, Error = Error> + Into<u8> + Copy,
35{
36    /// Creates a new `Codes` instance from an iterator of codes.
37    ///
38    /// # Panics
39    ///
40    /// Panics if the iterator is empty, as at least one code is required.
41    pub fn new<I: IntoIterator<Item = T>>(codes: I) -> Self {
42        let values: Vec<T> = codes.into_iter().collect();
43
44        if values.is_empty() {
45            panic!("At least one code is required");
46        }
47
48        Codes(values)
49    }
50
51    /// Decodes a `Codes` instance from a byte buffer.
52    pub(crate) fn decode(payload: &mut Bytes) -> Result<Self, Error> {
53        let mut codes: Vec<T> = Vec::with_capacity(payload.len());
54        while payload.has_remaining() {
55            codes.push(payload.get_u8().try_into()?);
56        }
57
58        if codes.is_empty() {
59            return Err(Error::NoCodes);
60        }
61
62        Ok(codes.into())
63    }
64
65    /// Encodes the `Codes` instance into a byte buffer.
66    pub(crate) fn encode(&self, buf: &mut BytesMut) {
67        self.0.iter().for_each(|&value| {
68            buf.put_u8(value.into());
69        });
70    }
71
72    /// Returns the number of codes in the `Codes` instance.
73    pub fn len(&self) -> usize {
74        self.0.len()
75    }
76}
77
78impl<T> AsRef<Vec<T>> for Codes<T> {
79    #[inline]
80    fn as_ref(&self) -> &Vec<T> {
81        self.0.as_ref()
82    }
83}
84
85impl<T> Borrow<Vec<T>> for Codes<T> {
86    fn borrow(&self) -> &Vec<T> {
87        self.0.as_ref()
88    }
89}
90
91impl<T> IntoIterator for Codes<T> {
92    type Item = T;
93    type IntoIter = std::vec::IntoIter<T>;
94
95    fn into_iter(self) -> Self::IntoIter {
96        self.0.into_iter()
97    }
98}
99
100impl<T> FromIterator<T> for Codes<T> {
101    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
102        Codes(Vec::from_iter(iter))
103    }
104}
105
106impl<T> From<Codes<T>> for Vec<T> {
107    #[inline]
108    fn from(value: Codes<T>) -> Self {
109        value.0
110    }
111}
112
113impl<T> From<Vec<T>> for Codes<T> {
114    #[inline]
115    fn from(value: Vec<T>) -> Self {
116        Codes(value)
117    }
118}
119
120/// A collection of MQTT topic filters used in subscription and unsubscription operations.
121///
122/// Topic filters follow MQTT specification rules:
123/// - May contain wildcards (`+` for single-level, `#` for multi-level)
124/// - Must be valid UTF-8 strings
125/// - Follow specific formatting rules for wildcard placement
126///
127/// # Examples
128///
129/// ```
130/// use mqute_codec::protocol::TopicFilters;
131///
132/// let filters = TopicFilters::new(vec!["sensors/temperature", "sensors/+/humidity"]);
133/// assert_eq!(filters.len(), 2);
134/// ```
135#[derive(Debug, Clone, PartialEq, Eq)]
136pub struct TopicFilters(Vec<String>);
137
138#[allow(clippy::len_without_is_empty)]
139impl TopicFilters {
140    /// Creates a new `TopicFilters` instance from an iterator of topic filters.
141    ///
142    /// # Panics
143    ///
144    /// Panics if:
145    /// - The iterator is empty (at least one topic filter is required)
146    /// - Any topic filter is invalid according to MQTT specification
147    pub fn new<T: IntoIterator<Item: Into<String>>>(filters: T) -> Self {
148        let values: Vec<String> = filters.into_iter().map(|x| x.into()).collect();
149
150        if values.is_empty() {
151            panic!("At least one topic filter is required");
152        }
153
154        // Validate all topic filters
155        for value in &values {
156            if !util::is_valid_topic_filter(&value) {
157                panic!("Invalid topic filter: '{}'", value);
158            }
159        }
160
161        TopicFilters(values)
162    }
163
164    /// Returns the number of topic filters in the `TopicFilters` instance.
165    pub fn len(&self) -> usize {
166        self.0.len()
167    }
168
169    /// Decodes a `TopicFilters` instance from a byte buffer.
170    pub(crate) fn decode(payload: &mut Bytes) -> Result<Self, Error> {
171        let mut filters = Vec::with_capacity(1);
172
173        while payload.has_remaining() {
174            let filter = decode_string(payload)?;
175
176            // Validate topic filter
177            if !util::is_valid_topic_filter(&filter) {
178                return Err(Error::InvalidTopicFilter(filter));
179            }
180
181            filters.push(filter);
182        }
183
184        if filters.is_empty() {
185            return Err(Error::NoTopic);
186        }
187
188        Ok(TopicFilters(filters))
189    }
190
191    /// Encodes the `TopicFilters` instance into a byte buffer.
192    pub(crate) fn encode(&self, buf: &mut BytesMut) {
193        self.0.iter().for_each(|filter| {
194            encode_string(buf, filter);
195        });
196    }
197
198    /// Calculates the encoded length of the `TopicFilters` instance.
199    pub(crate) fn encoded_len(&self) -> usize {
200        self.0.iter().fold(0, |acc, filter| acc + 2 + filter.len())
201    }
202}
203
204impl AsRef<Vec<String>> for TopicFilters {
205    #[inline]
206    fn as_ref(&self) -> &Vec<String> {
207        self.0.as_ref()
208    }
209}
210
211impl Borrow<Vec<String>> for TopicFilters {
212    fn borrow(&self) -> &Vec<String> {
213        self.0.as_ref()
214    }
215}
216
217impl IntoIterator for TopicFilters {
218    type Item = String;
219    type IntoIter = std::vec::IntoIter<String>;
220
221    fn into_iter(self) -> Self::IntoIter {
222        self.0.into_iter()
223    }
224}
225
226impl FromIterator<String> for TopicFilters {
227    fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
228        TopicFilters(Vec::from_iter(iter))
229    }
230}
231
232impl From<TopicFilters> for Vec<String> {
233    #[inline]
234    fn from(value: TopicFilters) -> Self {
235        value.0
236    }
237}
238
239impl From<Vec<String>> for TopicFilters {
240    #[inline]
241    fn from(value: Vec<String>) -> Self {
242        TopicFilters(value)
243    }
244}
245
246impl<T> Index<usize> for Codes<T> {
247    type Output = T;
248    fn index(&self, index: usize) -> &Self::Output {
249        self.0.index(index)
250    }
251}
252
253impl<T> IndexMut<usize> for Codes<T> {
254    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
255        self.0.index_mut(index)
256    }
257}