mqtt_tiny/packets/
unsubscribe.rs

1//! MQTT [`UNSUBSCRIBE`](https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718072)
2
3use crate::anyvec::AnyVec;
4use crate::coding::encoder::{PacketLenIter, TopicsIter, U16Iter, U8Iter, Unit};
5use crate::coding::length::Length;
6use crate::coding::{Decoder, Encoder};
7use crate::err;
8use crate::error::{Data, DecoderError, MemoryError};
9use crate::packets::TryFromIterator;
10use core::iter::Chain;
11use core::marker::PhantomData;
12
13/// An MQTT [`UNSUBSCRIBE` packet](https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718072)
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct Unsubscribe<Seq, Bytes> {
16    /// The packet ID
17    packet_id: u16,
18    /// A list of topic filters
19    topics: Seq,
20    /// The byte vector type
21    _vec: PhantomData<Bytes>,
22}
23impl<Seq, Bytes> Unsubscribe<Seq, Bytes>
24where
25    Seq: AnyVec<Bytes>,
26    Bytes: AnyVec<u8>,
27{
28    /// The packet type constant
29    pub const TYPE: u8 = 10;
30
31    /// Creates a new packet
32    pub fn new<S, T>(packet_id: u16, topics: S) -> Result<Self, MemoryError>
33    where
34        S: IntoIterator<Item = T>,
35        T: AsRef<[u8]>,
36    {
37        // Collect all topic-qos pairs
38        let mut topics_ = Seq::default();
39        for topic in topics {
40            // Copy topic and append pair
41            let topic = Bytes::new(topic.as_ref())?;
42            topics_.push(topic)?;
43        }
44
45        // Init self
46        Ok(Self { packet_id, topics: topics_, _vec: PhantomData })
47    }
48
49    /// The packet ID
50    pub fn packet_id(&self) -> u16 {
51        self.packet_id
52    }
53
54    /// A list of topic filters
55    pub fn topics(&self) -> &Seq {
56        &self.topics
57    }
58}
59impl<Seq, Bytes> TryFromIterator for Unsubscribe<Seq, Bytes>
60where
61    Seq: AnyVec<Bytes>,
62    Bytes: AnyVec<u8>,
63{
64    fn try_from_iter<T>(iter: T) -> Result<Self, DecoderError>
65    where
66        T: IntoIterator<Item = u8>,
67    {
68        // Read packet:
69        //  - header type and `2` flags
70        //  - packet len
71        //  - packed ID
72        //  - sequence
73        //     - topic filter
74        let mut decoder = Decoder::new(iter);
75        let (Self::TYPE, [false, false, true, false]) = decoder.header()? else {
76            return Err(err!(Data::SpecViolation, "invalid packet type/header"))?;
77        };
78        // Limit length and make decoder peekable
79        let len = decoder.packetlen()?;
80        let mut decoder = decoder.limit(len).peekable();
81        // Read fields
82        let packet_id = decoder.u16()?;
83        let topics = decoder.topics()?;
84
85        // Init self
86        Ok(Self { packet_id, topics, _vec: PhantomData })
87    }
88}
89impl<Seq, Bytes> IntoIterator for Unsubscribe<Seq, Bytes>
90where
91    Seq: AnyVec<Bytes>,
92    Bytes: AnyVec<u8>,
93{
94    type Item = u8;
95    #[rustfmt::skip]
96    type IntoIter =
97        // Complex iterator built out of the individual message fields
98        Chain<Chain<Chain<Chain<
99            // - header type and `2` flags
100            Unit, U8Iter>,
101            // - packet len
102            PacketLenIter>,
103            // - packed ID
104            U16Iter>,
105            // - sequence
106            //    - topic filter
107            TopicsIter<Seq, Bytes>>;
108
109    fn into_iter(self) -> Self::IntoIter {
110        // Precompute body length:
111        //  - packet ID
112        //  - sequence
113        //     - topic filter
114        #[rustfmt::skip]
115        let len = Length::new()
116            .u16(&self.packet_id)
117            .topics(&self.topics)
118            .into();
119
120        // Write packet:
121        //  - header type and `2` flags
122        //  - packet len
123        //  - packed ID
124        //  - sequence
125        //     - topic filter
126        Encoder::default()
127            .header(Self::TYPE, [false, false, true, false])
128            .packetlen(len)
129            .u16(self.packet_id)
130            .topics(self.topics)
131            .into_iter()
132    }
133}