simple_pub_sub_message/
header.rs

1/// Header of the simple_pub_sub_message packet
2use crate::{
3    constants::{self, *},
4    error::HeaderError,
5    PktType,
6};
7use anyhow::{anyhow, bail, Result};
8
9/// byte at index 0
10/// indicate the start of header
11const HEADER_START: usize = 0;
12
13/// byte at index 7
14/// indicate the end of header
15const HEADER_END: usize = 7;
16
17/// version is indicated using two bytes,
18/// first byte of the version
19/// byte at index 1
20const VERSION_BYTE_0: usize = 1;
21
22/// version is indicated using two bytes,
23/// second byte of the version
24/// byte at index 2
25const VERSION_BYTE_1: usize = 2;
26
27/// byte that indicates the packet type
28/// byte at index 3
29const PACKET_BYTE: usize = 3;
30
31/// byte that indicates the topic length
32/// byte at index 4
33const TOPIC_LENGTH_BYTE: usize = 4;
34
35/// first byte of the message length (big endian)
36/// byte at index 5
37const MESSAGE_LENGTH_BYTE_0: usize = 5;
38
39/// second byte of the message length (big endian)
40/// byte at index 6
41const MESSAGE_LENGTH_BYTE_1: usize = 6;
42
43/// start of the header
44/// value: 0x0F
45const HEADER_BYTE: u8 = 0x0F;
46
47/// end of header
48/// value: 0x00
49const PADDING_BYTE: u8 = 0x00;
50
51/// Header for the pub/sub packet
52/// total length 8 bytes.
53#[derive(Debug, Clone, PartialEq)]
54pub struct Header {
55    /// start byte of the packet, default value: 0x0F
56    pub header: u8,
57    /// pub-sub version: two bytes.
58    pub version: [u8; 2],
59    /// packet type: `PktType`
60    pub pkt_type: PktType,
61    /// topic length for publishing/subscribing/querying.
62    pub topic_length: u8,
63    /// message length: Max length 16 MB.
64    pub message_length: u16,
65    /// padding/endo of the header: 0x00
66    pub padding: u8,
67}
68
69impl Header {
70    /// creates a new `Header` with the given data.
71    /// ```
72    /// use simple_pub_sub_message::header::Header;
73    /// use simple_pub_sub_message::PktType;
74    /// Header::new(PktType::PUBLISH, 8, 20);
75    /// ```
76    pub fn new(pkt_type: PktType, topic_len: u8, message_len: u16) -> Header {
77        Header {
78            header: HEADER_BYTE,
79            version: DEFAULT_VERSION,
80            pkt_type,
81            topic_length: topic_len,
82            message_length: message_len,
83            padding: PADDING_BYTE,
84        }
85    }
86
87    /// returns a `Header` for the response `Msg`.
88    /// ```
89    /// use simple_pub_sub_message::header::Header;
90    /// use simple_pub_sub_message::PktType;
91    /// let header = Header::new(PktType::PUBLISH, 8, 20);
92    /// let response_header = header.response_header();
93    ///```
94    pub fn response_header(&self) -> Result<Header> {
95        let resp_type: PktType = match self.pkt_type {
96            PktType::SUBSCRIBE => PktType::SUBSCRIBEACK,
97            PktType::PUBLISH => PktType::PUBLISHACK,
98            PktType::UNSUBSCRIBE => PktType::UNSUBSCRIBEACK,
99            PktType::QUERY => PktType::QUERYRESP,
100            _ => {
101                return Err(anyhow!(HeaderError::InvalidResponseType));
102            }
103        };
104        Ok(Header {
105            header: HEADER_BYTE,
106            version: self.version,
107            pkt_type: resp_type,
108            topic_length: self.topic_length,
109            message_length: self.message_length,
110            padding: PADDING_BYTE,
111        })
112    }
113
114    /// returns the bytes for `Header`.
115    /// ```
116    /// use simple_pub_sub_message::header::Header;
117    /// use simple_pub_sub_message::PktType;
118    /// let header = Header::new(PktType::PUBLISH, 8, 20);
119    /// header.bytes();
120    ///```
121    pub fn bytes(&self) -> [u8; 8] {
122        let message_length_bytes = self.message_length.to_be_bytes();
123        [
124            self.header,
125            self.version[0],
126            self.version[1],
127            self.pkt_type.byte(),
128            self.topic_length,
129            message_length_bytes[0],
130            message_length_bytes[1],
131            self.padding,
132        ]
133    }
134}
135
136impl TryFrom<&[u8]> for Header {
137    type Error = anyhow::Error;
138
139    /// Parses a `Header` from a `&[u8]`
140    /// ```
141    /// use simple_pub_sub_message::header::Header;
142    /// Header::try_from([
143    ///        15,    // HEADER_BYTE`
144    ///        0, 1,  // `VERSION_BYTE_0`, `VERSION_BYTE_1`
145    ///        2,     // `PktType`
146    ///        3,     // `TOPIC_LENGTH_BYTE`
147    ///        0, 12, // `MESSAGE_LENGTH_BYTE_0`, `MESSAGE_LENGTH_BYTE_1`
148    ///        0,     // `PADDING_BYTE`
149    /// ].as_ref());
150    /// ```
151    fn try_from(bytes: &[u8]) -> Result<Header> {
152        if bytes.len() < constants::HEADER_LEN {
153            bail!(HeaderError::InvalidHeaderBufferLength);
154        }
155
156        if !(bytes[HEADER_START] == HEADER_BYTE && bytes[HEADER_END] == PADDING_BYTE) {
157            bail!(HeaderError::InvalidHeadOrTail);
158        }
159
160        if !SUPPORTED_VERSIONS.contains(&[bytes[VERSION_BYTE_0], bytes[VERSION_BYTE_1]]) {
161            bail!(HeaderError::UnsupportedVersion);
162        }
163
164        let pkt_type: PktType = match bytes[PACKET_BYTE] {
165            PUBLISH => PktType::PUBLISH,
166            SUBSCRIBE => PktType::SUBSCRIBE,
167            UNSUBSCRIBE => PktType::UNSUBSCRIBE,
168            QUERY => PktType::QUERY,
169            PUBLISHACK => PktType::PUBLISHACK,
170            SUBSCRIBEACK => PktType::SUBSCRIBEACK,
171            QUERYRESP => PktType::QUERYRESP,
172            _ => {
173                bail!(HeaderError::InvalidPacketType);
174            }
175        };
176
177        if bytes[TOPIC_LENGTH_BYTE] == 0 {
178            // topic would be absent for the response header
179            match pkt_type {
180                PktType::PUBLISH | PktType::SUBSCRIBE | PktType::UNSUBSCRIBE | PktType::QUERY => {
181                    bail!(HeaderError::InvalidTopicLength);
182                }
183                _ => {}
184            };
185        }
186
187        // calculate the message length
188        let message_length =
189            ((bytes[MESSAGE_LENGTH_BYTE_0] as u16) << 8) | bytes[MESSAGE_LENGTH_BYTE_1] as u16;
190
191        // message length can't be 0 for the publish
192        // or the query packet
193        if message_length == 0 {
194            match pkt_type {
195                PktType::PUBLISH => {
196                    bail!(HeaderError::InvalidMessageLength(0));
197                }
198                PktType::QUERY => {
199                    bail!(HeaderError::InvalidMessageLength(0));
200                }
201                _ => {}
202            };
203        }
204
205        Ok(Header {
206            header: HEADER_BYTE,
207            version: [bytes[VERSION_BYTE_0], bytes[VERSION_BYTE_1]],
208            pkt_type,
209            topic_length: bytes[TOPIC_LENGTH_BYTE],
210            message_length,
211            padding: PADDING_BYTE,
212        })
213    }
214}
215
216impl TryFrom<Vec<u8>> for Header {
217    type Error = anyhow::Error;
218
219    /// Parses a `Header` from a `Vec<u8>`.
220    /// ```
221    /// use simple_pub_sub_message::header::Header;
222    /// Header::try_from(vec![
223    ///        15,    // `HEADER_BYTE`
224    ///        0, 1,  // `VERSION_BYTE_0`, `VERSION_BYTE_1`
225    ///        2,     // `PktType`
226    ///        3,     // `TOPIC_LENGTH_BYTE`
227    ///        0, 12, // `MESSAGE_LENGTH_BYTE_0`, `MESSAGE_LENGTH_BYTE_1`
228    ///        0,     // `PADDING_BYTE`
229    /// ]);
230    /// ```
231    fn try_from(bytes: Vec<u8>) -> Result<Header> {
232        Header::try_from(&bytes[..])
233    }
234}