1use std::string::FromUtf8Error;
2use std::fmt;
3
4#[derive(Debug, Clone)]
5pub enum EncodeError{
6 TooLongTopic,
7 TtlNotAvailable,
8}
9
10#[derive(Debug, Clone)]
11pub enum DecodeError{
12 TooShortPackage,
13 TopicParsingError{err: FromUtf8Error},
14}
15
16impl fmt::Display for EncodeError{
17 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18 match self{
19 EncodeError::TooLongTopic => {
20 write!(f, "{}", "Topic is too long; Max topic length is 255 bytes")
21 }
22 EncodeError::TtlNotAvailable => {
23 write!(f, "{}", "Ttl option available only if cash flag enable")
24 }
25 }
26 }
27}
28
29impl fmt::Display for DecodeError{
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 match self{
32 DecodeError::TooShortPackage => {
33 write!(f, "{}", "Row package is too short to decode")
34 }
35 DecodeError::TopicParsingError{err} => {
36 std::fmt::Display::fmt(&err, f)
37 }
38 }
39 }
40}
41
42#[derive(Debug, Clone, Eq, PartialEq)]
43pub enum Package{
44 Subscribe{
45 topic: String,
46 is_regular: bool,
47 priority: u8,
48 is_subscribe: bool,
49 silent_mod: bool
50 },
51 RegularMsg{
52 topic: String,
53 priority: u8,
54 content: Vec<u8>,
55 cash: bool,
56 ttl: Option<u64>
57 },
58 ServiceMsg{
59 topic: String,
60 priority: u8,
61 count: u64,
62 }
63}
64
65impl Package{
66 pub fn encode(self) -> Result<Vec<u8>, EncodeError>{
67 let mut header: u8 = 0;
68 match self{
69 Package::Subscribe{
70 topic,
71 is_regular,
72 priority,
73 is_subscribe,
74 silent_mod
75 } => {
76 let topic_len = if topic.len() > u8::MAX as usize {
77 return Err(EncodeError::TooLongTopic)
78 } else { topic.len() as u8 };
79 if is_subscribe{
80 header |= 0b00100000;
81 }
82 if silent_mod{
83 header |= 0b00010000;
84 }
85 if is_regular{
86 header |= 0b00001000;
87 }
88 let mut result = vec![header, priority, topic_len];
89 result.append(&mut topic.into_bytes());
90 Ok(result)
91 }
92 Package::RegularMsg{
93 topic,
94 priority,
95 mut content,
96 cash,
97 ttl
98 } => {
99 let topic_len = if topic.len() > u8::MAX as usize {
100 return Err(EncodeError::TooLongTopic)
101 } else { topic.len() as u8 };
102 header |= 0b11000000;
103 if cash{
104 header |= 0b00001000;
105 if let Some(_) = ttl{
106 header |= 0b00000100;
107 }
108 }else if let Some(_) = ttl {
109 return Err(EncodeError::TtlNotAvailable)
110 }
111 let mut result = vec![header, priority, topic_len];
112 result.append(&mut topic.into_bytes());
113 match ttl{
114 None => {
115 result.append(&mut content);
116 }
117 Some(ttl) => {
118 result.append(&mut ttl.to_be_bytes().to_vec());
119 result.append(&mut content);
120 }
121 }
122 Ok(result)
123 }
124 Package::ServiceMsg{
125 topic,
126 priority,
127 count
128 } => {
129 let topic_len = if topic.len() > u8::MAX as usize {
130 return Err(EncodeError::TooLongTopic)
131 } else { topic.len() as u8 };
132 header |= 0b10000000;
133 let mut result = vec![header, priority, topic_len];
134 result.append(&mut topic.into_bytes());
135 result.append(&mut count.to_be_bytes().to_vec());
136 Ok(result)
137 }
138 }
139 }
140 pub fn decode(bytes: Vec<u8>) -> Result<Self, DecodeError>{
141 if bytes.len() < 3{
142 return Err(DecodeError::TooShortPackage)
143 }
144 let header = bytes[0];
145 let priority = bytes[1];
146 let topic_len = bytes[2];
147 let topic = {
148 let row = &bytes[3 .. 3+topic_len as usize];
149 match String::from_utf8(row.to_owned()){
150 Ok(topic) => {topic}
151 Err(err) => { return Err(DecodeError::TopicParsingError{err}) }
152 }
153 };
154 match header & 0b10000000 > 0{
155 true => {
156 match header & 0b01000000 > 0{
158 true => {
159 let cash = header & 0b00001000 > 0;
161 let ttl_exist = header & 0b00000100 > 0;
162 match ttl_exist && cash {
163 true => {
164 let ttl = {
165 let mut dst = [0u8; 8];
166 dst.clone_from_slice(&bytes[3+topic_len as usize .. 11+topic_len as usize]);
167 u64::from_be_bytes(dst)
168 };
169 let content = bytes[11+topic_len as usize .. bytes.len()].to_vec();
170 Ok(Package::RegularMsg{
171 topic,
172 priority,
173 content,
174 cash,
175 ttl: Some(ttl)
176 })
177 }
178 false => {
179 let content = bytes[3+topic_len as usize .. bytes.len()].to_vec();
180 Ok(Package::RegularMsg{
181 topic,
182 priority,
183 content,
184 cash,
185 ttl: None
186 })
187 }
188 }
189 }
190 false => {
191 let count = {
193 let mut dst = [0u8; 8];
194 dst.clone_from_slice(&bytes[3+topic_len as usize .. 11+topic_len as usize]);
195 u64::from_be_bytes(dst)
196 };
197 Ok(Package::ServiceMsg{
198 topic,
199 priority,
200 count
201 })
202 }
203 }
204 }
205 false => {
206 let is_subscribe = header & 0b00100000 > 0;
208 let silent_mod = header & 0b00010000 > 0;
209 let is_regular = header & 0b00001000 > 0;
210 Ok(Package::Subscribe{
211 topic,
212 is_regular,
213 priority,
214 is_subscribe,
215 silent_mod
216 })
217 }
218 }
219 }
220}
221
222#[cfg(test)]
223mod package_test{
224 use crate::codec::Package;
225
226 #[allow(dead_code)]
227 fn check_codec_correct(package: Package, expect_encode_error: bool, expect_decode_error: bool) {
228 match package.clone().encode() {
229 Ok(bytes) => {
230 match Package::decode(bytes) {
231 Ok(result_package) => {
232 assert_eq!(package, result_package)
233 }
234 Err(err) => {
235 if expect_decode_error{
236 assert!(true);
237 }else{
238 assert!(false, "Unexpected decode error: {}", err)
239 }
240 }
241 }
242 }
243 Err(err) => {
244 if expect_encode_error{
245 assert!(true);
246 }else{
247 assert!(false, "Unexpected encode error: {}", err)
248 }
249 }
250 }
251 }
252
253 #[test]
254 fn subscription_test(){
255 let package = Package::Subscribe{
256 topic: "some.topic".to_string(),
257 is_regular: true,
258 priority: 111,
259 is_subscribe: true,
260 silent_mod: true
261 };
262 check_codec_correct(package, false, false);
263 }
264
265 #[test]
266 fn subscription_service_test(){
267 let package = Package::Subscribe{
268 topic: "some.topic".to_string(),
269 is_regular: false,
270 priority: 111,
271 is_subscribe: true,
272 silent_mod: true
273 };
274 check_codec_correct(package, false, false);
275 }
276
277 #[test]
278 fn subscription_void_topic_test(){
279 let package = Package::Subscribe{
280 topic: "".to_string(),
281 is_regular: true,
282 priority: 111,
283 is_subscribe: true,
284 silent_mod: true
285 };
286 check_codec_correct(package, false, false);
287 }
288
289 #[test]
290 fn service_msg_test(){
291 let package = Package::ServiceMsg{
292 topic: "some.topic".to_string(),
293 priority: 111,
294 count: 222
295 };
296 check_codec_correct(package, false, false);
297 }
298
299 #[test]
300 fn service_void_topic_msg_test(){
301 let package = Package::ServiceMsg{
302 topic: "".to_string(),
303 priority: 111,
304 count: 222
305 };
306 check_codec_correct(package, false, false);
307 }
308
309 #[test]
310 fn regular_msg_test(){
311 let package = Package::RegularMsg{
312 topic: "some.topic".to_string(),
313 priority: 111,
314 content: vec![0, 1, 2, 3, 4, 5],
315 cash: true,
316 ttl: Some(1)
317 };
318 check_codec_correct(package, false, false);
319 }
320
321 #[test]
322 fn regular_void_topic_msg_test(){
323 let package = Package::RegularMsg{
324 topic: "".to_string(),
325 priority: 111,
326 content: vec![0, 1, 2, 3, 4, 5],
327 cash: true,
328 ttl: Some(1)
329 };
330 check_codec_correct(package, false, false);
331 }
332
333 #[test]
334 fn regular_void_ttl_msg_test(){
335 let package = Package::RegularMsg{
336 topic: "".to_string(),
337 priority: 111,
338 content: vec![0, 1, 2, 3, 4, 5],
339 cash: true,
340 ttl: None
341 };
342 check_codec_correct(package, false, false);
343 }
344
345 #[test]
346 fn regular_void_content_and_ttl_msg_test(){
347 let package = Package::RegularMsg{
348 topic: "".to_string(),
349 priority: 111,
350 content: vec![],
351 cash: true,
352 ttl: None
353 };
354 check_codec_correct(package, false, false);
355 }
356
357 #[test]
358 fn regular_invalid_ttl_using_msg_test(){
359 let package = Package::RegularMsg{
360 topic: "".to_string(),
361 priority: 111,
362 content: vec![],
363 cash: false,
364 ttl: Some(10)
365 };
366 check_codec_correct(package, true, false);
367 }
368}