mqtt_format/v3/
subscription_acks.rs

1//
2//   This Source Code Form is subject to the terms of the Mozilla Public
3//   License, v. 2.0. If a copy of the MPL was not distributed with this
4//   file, You can obtain one at http://mozilla.org/MPL/2.0/.
5//
6
7use futures::{AsyncWrite, AsyncWriteExt};
8use nom::{error::FromExternalError, multi::many1_count};
9
10use super::{
11    errors::{MPacketHeaderError, MPacketWriteError},
12    MSResult,
13};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct MSubscriptionAcks<'message> {
17    pub acks: &'message [MSubscriptionAck],
18}
19impl<'message> MSubscriptionAcks<'message> {
20    pub(crate) async fn write_to<W: AsyncWrite>(
21        &self,
22        writer: &mut std::pin::Pin<&mut W>,
23    ) -> Result<(), MPacketWriteError> {
24        writer
25            .write_all(unsafe {
26                // SAFETY: We know that MSubscriptionAck is repr u8, so we can safely transmute one
27                // slice into the other
28                std::mem::transmute(self.acks)
29            })
30            .await?;
31        Ok(())
32    }
33    pub(crate) fn get_len(&self) -> usize {
34        1
35    }
36}
37
38#[repr(u8)]
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum MSubscriptionAck {
41    MaximumQualityAtMostOnce = 0x00,
42    MaximumQualityAtLeastOnce = 0x01,
43    MaximumQualityExactlyOnce = 0x02,
44    Failure = 0x80,
45}
46
47fn msubscriptionack(input: &[u8]) -> MSResult<'_, MSubscriptionAck> {
48    let (input, data) = nom::number::complete::u8(input)?;
49
50    Ok((
51        input,
52        match data {
53            0x00 => MSubscriptionAck::MaximumQualityAtMostOnce,
54            0x01 => MSubscriptionAck::MaximumQualityAtLeastOnce,
55            0x02 => MSubscriptionAck::MaximumQualityExactlyOnce,
56            0x80 => MSubscriptionAck::Failure,
57            invalid_ack => {
58                return Err(nom::Err::Error(nom::error::Error::from_external_error(
59                    input,
60                    nom::error::ErrorKind::MapRes,
61                    MPacketHeaderError::InvalidSubscriptionAck(invalid_ack),
62                )))
63            }
64        },
65    ))
66}
67
68pub fn msubscriptionacks<'message>(
69    input: &'message [u8],
70) -> MSResult<'message, MSubscriptionAcks<'message>> {
71    let acks = input;
72    let (input, acks_len) = many1_count(msubscriptionack)(input)?;
73
74    assert!(acks_len <= acks.len());
75
76    let ack_ptr: *const MSubscriptionAck = acks.as_ptr() as *const MSubscriptionAck;
77    let acks: &'message [MSubscriptionAck] = unsafe {
78        // SAFETY: The array has been checked and is of the correct len, as well as
79        // MSubscriptionAck is the same repr and has no padding
80        std::slice::from_raw_parts(ack_ptr, acks_len)
81    };
82
83    Ok((input, MSubscriptionAcks { acks }))
84}
85
86#[cfg(test)]
87mod tests {
88    use crate::v3::subscription_acks::MSubscriptionAck;
89
90    use super::msubscriptionacks;
91
92    #[test]
93    fn check_valid_subacks() {
94        let input = &[0x1, 0x2, 0x0, 0x80];
95
96        let (rest, sub_acks) = msubscriptionacks(input).unwrap();
97
98        assert_eq!(rest, &[]);
99        assert_eq!(
100            sub_acks.acks,
101            &[
102                MSubscriptionAck::MaximumQualityAtLeastOnce,
103                MSubscriptionAck::MaximumQualityExactlyOnce,
104                MSubscriptionAck::MaximumQualityAtMostOnce,
105                MSubscriptionAck::Failure,
106            ]
107        )
108    }
109
110    #[test]
111    fn check_invalid_subacks() {
112        let input = &[0x1, 0x5];
113
114        nom::combinator::all_consuming(msubscriptionacks)(input).unwrap_err();
115    }
116}