mqute_codec/protocol/v4/
connack.rs

1//! # ConnAck Packet V4
2//!
3//! This module defines the `ConnectReturnCode` enum and the `ConnAck` struct, which are used
4//! in the MQTT protocol to represent the result of a connection request and the corresponding
5//! acknowledgment packet.
6
7use crate::codec::util::decode_byte;
8use crate::codec::{Decode, Encode, RawPacket};
9use crate::protocol::{FixedHeader, PacketType};
10use crate::Error;
11use bit_field::BitField;
12use bytes::{BufMut, BytesMut};
13
14/// Represents the return codes for a connection attempt in the MQTT protocol.
15///
16/// The `ConnectReturnCode` enum is used in the `ConnAck` packet to indicate the result
17/// of a client's connection request.
18#[derive(Debug, Copy, Clone, PartialEq, Eq)]
19#[repr(u8)]
20pub enum ConnectReturnCode {
21    /// Connection Accepted
22    Success = 0,
23
24    /// The Server does not support the level of the MQTT protocol requested by the Client
25    UnacceptableProtocolVersion,
26
27    /// The Client identifier is correct UTF-8 but not allowed by the Server
28    IdentifierRejected,
29
30    /// The Network Connection has been made but the MQTT service is unavailable
31    ServerUnavailable,
32
33    /// The data in the username or password is malformed
34    BadAuthData,
35
36    /// The Client is not authorized to connect
37    NotAuthorized,
38}
39
40impl TryFrom<u8> for ConnectReturnCode {
41    type Error = Error;
42
43    /// Converts a `u8` value into a `ConnectReturnCode`.
44    fn try_from(value: u8) -> Result<Self, Self::Error> {
45        let code = match value {
46            0 => ConnectReturnCode::Success,
47            1 => ConnectReturnCode::UnacceptableProtocolVersion,
48            2 => ConnectReturnCode::IdentifierRejected,
49            3 => ConnectReturnCode::ServerUnavailable,
50            4 => ConnectReturnCode::BadAuthData,
51            5 => ConnectReturnCode::NotAuthorized,
52            _ => return Err(Error::InvalidConnectReturnCode(value)),
53        };
54
55        Ok(code)
56    }
57}
58
59impl From<ConnectReturnCode> for u8 {
60    /// Converts a `ConnectReturnCode` into a `u8` value.
61    fn from(value: ConnectReturnCode) -> Self {
62        value as u8
63    }
64}
65
66/// Represents an MQTT `ConnAck` packet.
67///
68/// # Example
69///
70/// ```rust
71/// use mqute_codec::protocol::v4::{ConnectReturnCode, ConnAck};
72///
73/// let connack = ConnAck::new(ConnectReturnCode::Success, true);
74/// assert_eq!(connack.code(), ConnectReturnCode::Success);
75/// ```
76#[derive(Debug, Copy, Clone, PartialEq, Eq)]
77pub struct ConnAck {
78    code: ConnectReturnCode,
79    session_present: bool,
80}
81
82impl ConnAck {
83    /// Creates a new `ConnAck` packet with the specified return code and session present flag.
84    pub fn new(code: ConnectReturnCode, session_present: bool) -> Self {
85        ConnAck {
86            code,
87            session_present,
88        }
89    }
90
91    /// Returns the `ConnectReturnCode` contained in the `ConnAck` packet.
92    pub fn code(&self) -> ConnectReturnCode {
93        self.code
94    }
95
96    /// Returns the `session_present` flag from the `ConnAck` packet.
97    pub fn session_present(&self) -> bool {
98        self.session_present
99    }
100}
101
102impl Decode for ConnAck {
103    /// Decodes a `ConnAck` packet from a raw MQTT packet.
104    fn decode(mut packet: RawPacket) -> Result<Self, Error> {
105        if packet.header.packet_type() != PacketType::ConnAck || !packet.header.flags().is_default()
106        {
107            return Err(Error::MalformedPacket);
108        }
109
110        let conn_ack_flag = decode_byte(&mut packet.payload)?;
111        let ret_code = decode_byte(&mut packet.payload)?;
112        let code = ret_code.try_into()?;
113        let session_present = conn_ack_flag.get_bit(0);
114
115        Ok(ConnAck {
116            code,
117            session_present,
118        })
119    }
120}
121
122impl Encode for ConnAck {
123    /// Encodes the `ConnAck` packet into a byte buffer.
124    fn encode(&self, buf: &mut BytesMut) -> Result<(), Error> {
125        let header = FixedHeader::new(PacketType::ConnAck, self.payload_len());
126        header.encode(buf)?;
127
128        let mut flags = 0u8;
129        flags.set_bit(0, self.session_present);
130
131        // Write the session present flag
132        buf.put_u8(flags);
133
134        // Write the connect return code
135        buf.put_u8(self.code.into());
136        Ok(())
137    }
138
139    /// Returns the length of the `ConnAck` packet payload.
140    fn payload_len(&self) -> usize {
141        2
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148    use crate::codec::PacketCodec;
149    use bytes::BytesMut;
150    use tokio_util::codec::Decoder;
151
152    #[test]
153    fn connack_decode() {
154        let mut codec = PacketCodec::new(None, None);
155
156        let data = &[
157            (PacketType::ConnAck as u8) << 4, // Packet type
158            0x02,                             // Remaining len
159            0x01,                             // Connect Acknowledge Flags
160            0x00,                             // Connect Return code
161        ];
162
163        let mut stream = BytesMut::new();
164
165        stream.extend_from_slice(&data[..]);
166
167        let raw_packet = codec.decode(&mut stream).unwrap().unwrap();
168        let packet = ConnAck::decode(raw_packet).unwrap();
169
170        assert_eq!(packet, ConnAck::new(ConnectReturnCode::Success, true));
171    }
172
173    #[test]
174    fn connack_encode() {
175        let packet = ConnAck::new(ConnectReturnCode::Success, true);
176
177        let mut stream = BytesMut::new();
178        packet.encode(&mut stream).unwrap();
179        assert_eq!(
180            stream,
181            vec![(PacketType::ConnAck as u8) << 4, 0x02, 0x01, 0x00]
182        );
183    }
184}