mqtt_codec_kit/common/variable_header/
connect_flags.rs

1use std::io::{self, Read, Write};
2
3use byteorder::{ReadBytesExt, WriteBytesExt};
4
5use crate::common::{Decodable, Encodable};
6
7/// Flags for `CONNECT` packet
8#[derive(Debug, Eq, PartialEq, Copy, Clone)]
9pub struct ConnectFlags {
10    pub username: bool,
11    pub password: bool,
12    pub will_retain: bool,
13    pub will_qos: u8,
14    pub will_flag: bool,
15    pub clean_session: bool,
16    // We never use this, but must decode because brokers must verify it's zero per [MQTT-3.1.2-3]
17    pub reserved: bool,
18}
19
20impl ConnectFlags {
21    pub fn empty() -> ConnectFlags {
22        ConnectFlags {
23            username: false,
24            password: false,
25            will_retain: false,
26            will_qos: 0,
27            will_flag: false,
28            clean_session: false,
29            reserved: false,
30        }
31    }
32}
33
34impl Encodable for ConnectFlags {
35    fn encode<W: Write>(&self, writer: &mut W) -> Result<(), io::Error> {
36        let code = ((self.username as u8) << 7)
37            | ((self.password as u8) << 6)
38            | ((self.will_retain as u8) << 5)
39            | ((self.will_qos) << 3)
40            | ((self.will_flag as u8) << 2)
41            | ((self.clean_session as u8) << 1);
42
43        writer.write_u8(code)
44    }
45
46    fn encoded_length(&self) -> u32 {
47        1
48    }
49}
50
51impl Decodable for ConnectFlags {
52    type Error = ConnectFlagsError;
53    type Cond = ();
54
55    fn decode_with<R: Read>(reader: &mut R, _rest: ()) -> Result<ConnectFlags, ConnectFlagsError> {
56        let code = reader.read_u8()?;
57        if code & 1 != 0 {
58            return Err(ConnectFlagsError::InvalidReservedFlag);
59        }
60
61        Ok(ConnectFlags {
62            username: (code & 0b1000_0000) != 0,
63            password: (code & 0b0100_0000) != 0,
64            will_retain: (code & 0b0010_0000) != 0,
65            will_qos: (code & 0b0001_1000) >> 3,
66            will_flag: (code & 0b0000_0100) != 0,
67            clean_session: (code & 0b0000_0010) != 0,
68            reserved: (code & 0b0000_0001) != 0,
69        })
70    }
71}
72
73#[derive(Debug, thiserror::Error)]
74#[error(transparent)]
75pub enum ConnectFlagsError {
76    IoError(#[from] io::Error),
77    #[error("invalid reserved flag")]
78    InvalidReservedFlag,
79}