rust-mqtt 0.5.1

MQTT client for embedded and non-embedded environments
Documentation
use core::marker::PhantomData;

use crate::{
    buffer::BufferProvider,
    eio::{Read, Write},
    fmt::trace,
    header::{FixedHeader, PacketType},
    io::{read::BodyReader, write::Writable},
    packet::{Packet, RxError, RxPacket, TxError, TxPacket},
    types::VarByteInt,
    v5::packet::pings::types::{PingPacketType, Req, Resp},
};

mod types;

#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct GenericPingPacket<T: PingPacketType> {
    phantom_data: PhantomData<T>,
}

pub type PingreqPacket = GenericPingPacket<Req>;
pub type PingrespPacket = GenericPingPacket<Resp>;

impl<T: PingPacketType> Packet for GenericPingPacket<T> {
    const PACKET_TYPE: PacketType = T::PACKET_TYPE;
}
impl<'p, T: PingPacketType> RxPacket<'p> for GenericPingPacket<T> {
    async fn receive<R: Read, B: BufferProvider<'p>>(
        header: &FixedHeader,
        _: BodyReader<'_, 'p, R, B>,
    ) -> Result<Self, RxError<R::Error, B::ProvisionError>> {
        trace!("decoding {:?} packet", T::PACKET_TYPE);

        if header.flags() == T::FLAGS {
            Ok(Self {
                phantom_data: PhantomData,
            })
        } else {
            trace!(
                "invalid {:?} fixed header flags: {}",
                T::PACKET_TYPE,
                header.flags()
            );
            Err(RxError::MalformedPacket)
        }
    }
}
impl<T: PingPacketType> TxPacket for GenericPingPacket<T> {
    fn remaining_len(&self) -> VarByteInt {
        // Invariant: 0 < VarByteInt::MAX_ENCODABLE
        VarByteInt::new_unchecked(0)
    }

    async fn send<W: Write>(&self, write: &mut W) -> Result<(), TxError<W::Error>> {
        FixedHeader::new(Self::PACKET_TYPE, T::FLAGS, self.remaining_len())
            .write(write)
            .await?;

        Ok(())
    }
}

impl<T: PingPacketType> GenericPingPacket<T> {
    pub fn new() -> Self {
        Self {
            phantom_data: PhantomData,
        }
    }
}

#[cfg(test)]
mod unit {
    mod req {
        use crate::{
            test::{rx::decode, tx::encode},
            v5::packet::PingreqPacket,
        };

        #[tokio::test]
        #[test_log::test]
        async fn encode() {
            #[rustfmt::skip]
            encode!(
                PingreqPacket::new(),
                [
                    0xC0, //
                    0x00, // remaining length
                ]
            );
        }

        #[tokio::test]
        #[test_log::test]
        async fn decode() {
            decode!(PingreqPacket, 0, [0xC0, 0x00]);
        }
    }

    mod resp {
        use crate::{
            test::{rx::decode, tx::encode},
            v5::packet::PingrespPacket,
        };

        #[tokio::test]
        #[test_log::test]
        async fn encode() {
            #[rustfmt::skip]
            encode!(
                PingrespPacket::new(),
                [
                    0xD0, //
                    0x00, // remaining length
                ]
            );
        }

        #[tokio::test]
        #[test_log::test]
        async fn decode() {
            decode!(PingrespPacket, 0, [0xD0, 0x00]);
        }
    }
}