mqtt/control/
packet_type.rs

1//! Packet types
2
3use crate::qos::QualityOfService;
4
5/// Packet type
6// INVARIANT: the high 4 bits of the byte must be a valid control type
7#[derive(Debug, Eq, PartialEq, Copy, Clone)]
8pub struct PacketType(u8);
9
10/// Defined control types
11#[rustfmt::skip]
12#[repr(u8)]
13#[derive(Debug, Eq, PartialEq, Copy, Clone)]
14pub enum ControlType {
15    /// Client request to connect to Server
16    Connect                         = value::CONNECT,
17
18    /// Connect acknowledgment
19    ConnectAcknowledgement          = value::CONNACK,
20
21    /// Publish message
22    Publish                         = value::PUBLISH,
23
24    /// Publish acknowledgment
25    PublishAcknowledgement          = value::PUBACK,
26
27    /// Publish received (assured delivery part 1)
28    PublishReceived                 = value::PUBREC,
29
30    /// Publish release (assured delivery part 2)
31    PublishRelease                  = value::PUBREL,
32
33    /// Publish complete (assured delivery part 3)
34    PublishComplete                 = value::PUBCOMP,
35
36    /// Client subscribe request
37    Subscribe                       = value::SUBSCRIBE,
38
39    /// Subscribe acknowledgment
40    SubscribeAcknowledgement        = value::SUBACK,
41
42    /// Unsubscribe request
43    Unsubscribe                     = value::UNSUBSCRIBE,
44
45    /// Unsubscribe acknowledgment
46    UnsubscribeAcknowledgement      = value::UNSUBACK,
47
48    /// PING request
49    PingRequest                     = value::PINGREQ,
50
51    /// PING response
52    PingResponse                    = value::PINGRESP,
53
54    /// Client is disconnecting
55    Disconnect                      = value::DISCONNECT,
56}
57
58impl ControlType {
59    #[inline]
60    fn default_flags(self) -> u8 {
61        match self {
62            ControlType::Connect => 0,
63            ControlType::ConnectAcknowledgement => 0,
64
65            ControlType::Publish => 0,
66            ControlType::PublishAcknowledgement => 0,
67            ControlType::PublishReceived => 0,
68            ControlType::PublishRelease => 0b0010,
69            ControlType::PublishComplete => 0,
70
71            ControlType::Subscribe => 0b0010,
72            ControlType::SubscribeAcknowledgement => 0,
73
74            ControlType::Unsubscribe => 0b0010,
75            ControlType::UnsubscribeAcknowledgement => 0,
76
77            ControlType::PingRequest => 0,
78            ControlType::PingResponse => 0,
79
80            ControlType::Disconnect => 0,
81        }
82    }
83}
84
85impl PacketType {
86    /// Creates a packet type. Returns None if `flags` is an invalid value for the given
87    /// ControlType as defined by the [MQTT spec].
88    ///
89    /// [MQTT spec]: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Table_2.2_-
90    pub fn new(t: ControlType, flags: u8) -> Result<PacketType, InvalidFlag> {
91        let flags_ok = match t {
92            ControlType::Publish => {
93                let qos = (flags & 0b0110) >> 1;
94                matches!(qos, 0 | 1 | 2)
95            }
96            _ => t.default_flags() == flags,
97        };
98        if flags_ok {
99            Ok(PacketType::new_unchecked(t, flags))
100        } else {
101            Err(InvalidFlag(t, flags))
102        }
103    }
104
105    #[inline]
106    fn new_unchecked(t: ControlType, flags: u8) -> PacketType {
107        let byte = (t as u8) << 4 | (flags & 0x0F);
108        #[allow(unused_unsafe)]
109        unsafe {
110            // SAFETY: just constructed from a valid ControlType
111            PacketType(byte)
112        }
113    }
114
115    /// Creates a packet type with default flags
116    ///
117    /// <http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Table_2.2_->
118    #[inline]
119    pub fn with_default(t: ControlType) -> PacketType {
120        let flags = t.default_flags();
121        PacketType::new_unchecked(t, flags)
122    }
123
124    pub(crate) fn publish(qos: QualityOfService) -> PacketType {
125        PacketType::new_unchecked(ControlType::Publish, (qos as u8) << 1)
126    }
127
128    #[inline]
129    pub(crate) fn update_flags(&mut self, upd: impl FnOnce(u8) -> u8) {
130        let flags = upd(self.flags());
131        self.0 = (self.0 & !0x0F) | (flags & 0x0F)
132    }
133
134    /// To code
135    #[inline]
136    pub fn to_u8(self) -> u8 {
137        self.0
138    }
139
140    /// From code
141    pub fn from_u8(val: u8) -> Result<PacketType, PacketTypeError> {
142        let type_val = val >> 4;
143        let flags = val & 0x0F;
144
145        let control_type = get_control_type(type_val).ok_or(PacketTypeError::ReservedType(type_val, flags))?;
146        Ok(PacketType::new(control_type, flags)?)
147    }
148
149    #[inline]
150    pub fn control_type(self) -> ControlType {
151        get_control_type(self.0 >> 4).unwrap_or_else(|| {
152            // SAFETY: this is maintained by the invariant for PacketType
153            unsafe { std::hint::unreachable_unchecked() }
154        })
155    }
156
157    #[inline]
158    pub fn flags(self) -> u8 {
159        self.0 & 0x0F
160    }
161}
162
163#[inline]
164fn get_control_type(val: u8) -> Option<ControlType> {
165    let typ = match val {
166        value::CONNECT => ControlType::Connect,
167        value::CONNACK => ControlType::ConnectAcknowledgement,
168
169        value::PUBLISH => ControlType::Publish,
170        value::PUBACK => ControlType::PublishAcknowledgement,
171        value::PUBREC => ControlType::PublishReceived,
172        value::PUBREL => ControlType::PublishRelease,
173        value::PUBCOMP => ControlType::PublishComplete,
174
175        value::SUBSCRIBE => ControlType::Subscribe,
176        value::SUBACK => ControlType::SubscribeAcknowledgement,
177
178        value::UNSUBSCRIBE => ControlType::Unsubscribe,
179        value::UNSUBACK => ControlType::UnsubscribeAcknowledgement,
180
181        value::PINGREQ => ControlType::PingRequest,
182        value::PINGRESP => ControlType::PingResponse,
183
184        value::DISCONNECT => ControlType::Disconnect,
185
186        _ => return None,
187    };
188    Some(typ)
189}
190
191/// Parsing packet type errors
192#[derive(Debug, thiserror::Error)]
193pub enum PacketTypeError {
194    #[error("reserved type {0:?} (flags {1:#X})")]
195    ReservedType(u8, u8),
196    #[error(transparent)]
197    InvalidFlag(#[from] InvalidFlag),
198}
199
200#[derive(Debug, thiserror::Error)]
201#[error("invalid flag for {0:?} ({1:#X})")]
202pub struct InvalidFlag(pub ControlType, pub u8);
203
204#[rustfmt::skip]
205mod value {
206    pub const CONNECT:     u8 = 1;
207    pub const CONNACK:     u8 = 2;
208    pub const PUBLISH:     u8 = 3;
209    pub const PUBACK:      u8 = 4;
210    pub const PUBREC:      u8 = 5;
211    pub const PUBREL:      u8 = 6;
212    pub const PUBCOMP:     u8 = 7;
213    pub const SUBSCRIBE:   u8 = 8;
214    pub const SUBACK:      u8 = 9;
215    pub const UNSUBSCRIBE: u8 = 10;
216    pub const UNSUBACK:    u8 = 11;
217    pub const PINGREQ:     u8 = 12;
218    pub const PINGRESP:    u8 = 13;
219    pub const DISCONNECT:  u8 = 14;
220}