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}