Skip to main content

rust_mqtt/
header.rs

1//! Contains types representing the MQTT fixed header.
2
3use crate::{
4    eio::Write,
5    fmt::unreachable,
6    io::{
7        err::WriteError,
8        write::{Writable, wlen},
9    },
10    types::VarByteInt,
11};
12
13/// The fixed header of any MQTT Control Packet containing the packet type, flags
14/// and the remaining length.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17pub struct FixedHeader {
18    pub(crate) type_and_flags: u8,
19    pub(crate) remaining_len: VarByteInt,
20}
21
22impl Writable for FixedHeader {
23    fn written_len(&self) -> usize {
24        wlen!(u8) + self.remaining_len.written_len()
25    }
26
27    async fn write<W: Write>(&self, write: &mut W) -> Result<(), WriteError<W::Error>> {
28        self.type_and_flags.write(write).await?;
29        self.remaining_len.write(write).await?;
30
31        Ok(())
32    }
33}
34
35impl FixedHeader {
36    pub(crate) fn new(packet_type: PacketType, flags: u8, remaining_len: VarByteInt) -> Self {
37        let packet_type = (packet_type as u8) << 4;
38        Self {
39            type_and_flags: packet_type | flags,
40            remaining_len,
41        }
42    }
43
44    /// Returns the flags of the [`FixedHeader`]. These are the lower 4 bits of the first byte.
45    /// The type of the packet is masked away.
46    #[must_use]
47    pub fn flags(&self) -> u8 {
48        self.type_and_flags & 0x0F
49    }
50
51    /// Returns the [`PacketType`]
52    ///
53    /// # Errors
54    ///
55    /// Returns [`Reserved`] if the value of the packet type bits is reserved.
56    pub fn packet_type(&self) -> Result<PacketType, Reserved> {
57        PacketType::from_type_and_flags(self.type_and_flags)
58    }
59}
60
61/// Returned if [`PacketType`] is reserved.
62#[derive(Debug)]
63#[cfg_attr(feature = "defmt", derive(defmt::Format))]
64pub struct Reserved;
65
66/// An MQTT Control Packet type.
67#[allow(missing_docs)]
68#[derive(PartialEq, Eq)]
69pub enum PacketType {
70    Connect = 1,
71    Connack = 2,
72    Publish = 3,
73    Puback = 4,
74    Pubrec = 5,
75    Pubrel = 6,
76    Pubcomp = 7,
77    Subscribe = 8,
78    Suback = 9,
79    Unsubscribe = 10,
80    Unsuback = 11,
81    Pingreq = 12,
82    Pingresp = 13,
83    Disconnect = 14,
84
85    #[cfg(feature = "v5")]
86    Auth = 15,
87}
88
89impl core::fmt::Debug for PacketType {
90    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
91        match self {
92            Self::Connect => write!(f, "CONNECT"),
93            Self::Connack => write!(f, "CONNACK"),
94            Self::Publish => write!(f, "PUBLISH"),
95            Self::Puback => write!(f, "PUBACK"),
96            Self::Pubrec => write!(f, "PUBREC"),
97            Self::Pubrel => write!(f, "PUBREL"),
98            Self::Pubcomp => write!(f, "PUBCOMP"),
99            Self::Subscribe => write!(f, "SUBSCRIBE"),
100            Self::Suback => write!(f, "SUBACK"),
101            Self::Unsubscribe => write!(f, "UNSUBSCRIBE"),
102            Self::Unsuback => write!(f, "UNSUBACK"),
103            Self::Pingreq => write!(f, "PINGREQ"),
104            Self::Pingresp => write!(f, "PINGRESP"),
105            Self::Disconnect => write!(f, "DISCONNECT"),
106
107            #[cfg(feature = "v5")]
108            Self::Auth => write!(f, "AUTH"),
109        }
110    }
111}
112
113#[cfg(feature = "defmt")]
114impl defmt::Format for PacketType {
115    fn format(&self, fmt: defmt::Formatter) {
116        match self {
117            Self::Connect => defmt::write!(fmt, "CONNECT"),
118            Self::Connack => defmt::write!(fmt, "CONNACK"),
119            Self::Publish => defmt::write!(fmt, "PUBLISH"),
120            Self::Puback => defmt::write!(fmt, "PUBACK"),
121            Self::Pubrec => defmt::write!(fmt, "PUBREC"),
122            Self::Pubrel => defmt::write!(fmt, "PUBREL"),
123            Self::Pubcomp => defmt::write!(fmt, "PUBCOMP"),
124            Self::Subscribe => defmt::write!(fmt, "SUBSCRIBE"),
125            Self::Suback => defmt::write!(fmt, "SUBACK"),
126            Self::Unsubscribe => defmt::write!(fmt, "UNSUBSCRIBE"),
127            Self::Unsuback => defmt::write!(fmt, "UNSUBACK"),
128            Self::Pingreq => defmt::write!(fmt, "PINGREQ"),
129            Self::Pingresp => defmt::write!(fmt, "PINGRESP"),
130            Self::Disconnect => defmt::write!(fmt, "DISCONNECT"),
131
132            #[cfg(feature = "v5")]
133            Self::Auth => defmt::write!(fmt, "AUTH"),
134        }
135    }
136}
137
138impl PacketType {
139    pub(crate) fn from_type_and_flags(type_and_flags: u8) -> Result<Self, Reserved> {
140        match type_and_flags >> 4 {
141            0 => Err(Reserved),
142            1 => Ok(PacketType::Connect),
143            2 => Ok(PacketType::Connack),
144            3 => Ok(PacketType::Publish),
145            4 => Ok(PacketType::Puback),
146            5 => Ok(PacketType::Pubrec),
147            6 => Ok(PacketType::Pubrel),
148            7 => Ok(PacketType::Pubcomp),
149            8 => Ok(PacketType::Subscribe),
150            9 => Ok(PacketType::Suback),
151            10 => Ok(PacketType::Unsubscribe),
152            11 => Ok(PacketType::Unsuback),
153            12 => Ok(PacketType::Pingreq),
154            13 => Ok(PacketType::Pingresp),
155            14 => Ok(PacketType::Disconnect),
156
157            #[cfg(feature = "v3")]
158            15 => Err(Reserved),
159
160            #[cfg(feature = "v5")]
161            15 => Ok(PacketType::Auth),
162
163            0x10.. => unreachable!("u8 shifted 4 bits: all values 0x0..=0xF are covered"),
164        }
165    }
166}