rust-mqtt 0.5.1

MQTT client for embedded and non-embedded environments
Documentation
//! Contains types representing the MQTT fixed header.

use crate::{
    eio::Write,
    fmt::unreachable,
    io::{
        err::WriteError,
        write::{Writable, wlen},
    },
    types::VarByteInt,
};

/// The fixed header of any MQTT Control Packet containing the packet type, flags
/// and the remaining length.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FixedHeader {
    pub(crate) type_and_flags: u8,
    pub(crate) remaining_len: VarByteInt,
}

impl Writable for FixedHeader {
    fn written_len(&self) -> usize {
        wlen!(u8) + self.remaining_len.written_len()
    }

    async fn write<W: Write>(&self, write: &mut W) -> Result<(), WriteError<W::Error>> {
        self.type_and_flags.write(write).await?;
        self.remaining_len.write(write).await?;

        Ok(())
    }
}

impl FixedHeader {
    pub(crate) fn new(packet_type: PacketType, flags: u8, remaining_len: VarByteInt) -> Self {
        let packet_type = (packet_type as u8) << 4;
        Self {
            type_and_flags: packet_type | flags,
            remaining_len,
        }
    }

    /// Returns the flags of the [`FixedHeader`]. These are the lower 4 bits of the first byte.
    /// The type of the packet is masked away.
    #[must_use]
    pub fn flags(&self) -> u8 {
        self.type_and_flags & 0x0F
    }

    /// Returns the [`PacketType`]
    ///
    /// # Errors
    ///
    /// Returns [`Reserved`] if the value of the packet type bits is reserved.
    pub fn packet_type(&self) -> Result<PacketType, Reserved> {
        PacketType::from_type_and_flags(self.type_and_flags)
    }
}

/// Returned if [`PacketType`] is reserved.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Reserved;

/// An MQTT Control Packet type.
#[allow(missing_docs)]
#[derive(PartialEq, Eq)]
pub enum PacketType {
    Connect = 1,
    Connack = 2,
    Publish = 3,
    Puback = 4,
    Pubrec = 5,
    Pubrel = 6,
    Pubcomp = 7,
    Subscribe = 8,
    Suback = 9,
    Unsubscribe = 10,
    Unsuback = 11,
    Pingreq = 12,
    Pingresp = 13,
    Disconnect = 14,

    #[cfg(feature = "v5")]
    Auth = 15,
}

impl core::fmt::Debug for PacketType {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::Connect => write!(f, "CONNECT"),
            Self::Connack => write!(f, "CONNACK"),
            Self::Publish => write!(f, "PUBLISH"),
            Self::Puback => write!(f, "PUBACK"),
            Self::Pubrec => write!(f, "PUBREC"),
            Self::Pubrel => write!(f, "PUBREL"),
            Self::Pubcomp => write!(f, "PUBCOMP"),
            Self::Subscribe => write!(f, "SUBSCRIBE"),
            Self::Suback => write!(f, "SUBACK"),
            Self::Unsubscribe => write!(f, "UNSUBSCRIBE"),
            Self::Unsuback => write!(f, "UNSUBACK"),
            Self::Pingreq => write!(f, "PINGREQ"),
            Self::Pingresp => write!(f, "PINGRESP"),
            Self::Disconnect => write!(f, "DISCONNECT"),

            #[cfg(feature = "v5")]
            Self::Auth => write!(f, "AUTH"),
        }
    }
}

#[cfg(feature = "defmt")]
impl defmt::Format for PacketType {
    fn format(&self, fmt: defmt::Formatter) {
        match self {
            Self::Connect => defmt::write!(fmt, "CONNECT"),
            Self::Connack => defmt::write!(fmt, "CONNACK"),
            Self::Publish => defmt::write!(fmt, "PUBLISH"),
            Self::Puback => defmt::write!(fmt, "PUBACK"),
            Self::Pubrec => defmt::write!(fmt, "PUBREC"),
            Self::Pubrel => defmt::write!(fmt, "PUBREL"),
            Self::Pubcomp => defmt::write!(fmt, "PUBCOMP"),
            Self::Subscribe => defmt::write!(fmt, "SUBSCRIBE"),
            Self::Suback => defmt::write!(fmt, "SUBACK"),
            Self::Unsubscribe => defmt::write!(fmt, "UNSUBSCRIBE"),
            Self::Unsuback => defmt::write!(fmt, "UNSUBACK"),
            Self::Pingreq => defmt::write!(fmt, "PINGREQ"),
            Self::Pingresp => defmt::write!(fmt, "PINGRESP"),
            Self::Disconnect => defmt::write!(fmt, "DISCONNECT"),

            #[cfg(feature = "v5")]
            Self::Auth => defmt::write!(fmt, "AUTH"),
        }
    }
}

impl PacketType {
    pub(crate) fn from_type_and_flags(type_and_flags: u8) -> Result<Self, Reserved> {
        match type_and_flags >> 4 {
            0 => Err(Reserved),
            1 => Ok(PacketType::Connect),
            2 => Ok(PacketType::Connack),
            3 => Ok(PacketType::Publish),
            4 => Ok(PacketType::Puback),
            5 => Ok(PacketType::Pubrec),
            6 => Ok(PacketType::Pubrel),
            7 => Ok(PacketType::Pubcomp),
            8 => Ok(PacketType::Subscribe),
            9 => Ok(PacketType::Suback),
            10 => Ok(PacketType::Unsubscribe),
            11 => Ok(PacketType::Unsuback),
            12 => Ok(PacketType::Pingreq),
            13 => Ok(PacketType::Pingresp),
            14 => Ok(PacketType::Disconnect),

            #[cfg(feature = "v3")]
            15 => Err(Reserved),

            #[cfg(feature = "v5")]
            15 => Ok(PacketType::Auth),

            0x10.. => unreachable!("u8 shifted 4 bits: all values 0x0..=0xF are covered"),
        }
    }
}