mbp/
codec.rs

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                //Msg
157                match header & 0b01000000 > 0{
158                    true => {
159                        //Regular
160                        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                        //Service
192                        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                //Sub
207                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}