eb90 0.1.1

EB90 Framing Protocol library for Rust
Documentation
use bytes::{Bytes, BytesMut};

use crate::{
    crc,
    parser::{self, Error, JunkKind, Parser},
    ETX, STX,
};

pub struct Encoder(());

impl Encoder {
    #[allow(clippy::new_without_default)]
    pub fn new() -> Self {
        Self(())
    }
}

impl<T> tokio_util::codec::Encoder<T> for Encoder
where
    T: AsRef<[u8]>,
{
    type Error = std::io::Error;

    fn encode(&mut self, item: T, dst: &mut BytesMut) -> Result<(), Self::Error> {
        let bytes = item.as_ref();
        dst.extend_from_slice(&STX);
        if bytes.len() > u16::MAX as usize {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                format!("body length {} is too large.", bytes.len()),
            ));
        }
        dst.extend_from_slice(&(bytes.len() as u16).to_be_bytes());
        dst.extend_from_slice(bytes);
        let checksum = crc::ALGO.checksum(bytes);
        dst.extend_from_slice(&checksum.to_be_bytes());
        dst.extend_from_slice(&ETX);
        Ok(())
    }
}

pub struct Decoder<B> {
    parser: Parser<B>,
}

impl<B> Decoder<B>
where
    B: parser::Buffer,
{
    pub fn new(buf: B) -> Self {
        let parser = Parser::with_buffer(buf);
        Self { parser }
    }
}

impl<B> tokio_util::codec::Decoder for Decoder<B>
where
    B: parser::Buffer,
{
    type Item = Decoded;
    type Error = std::io::Error;

    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        let filled_len = self.parser.fill(src);
        let _ = src.split_to(filled_len);
        match self.parser.read() {
            Err(Error::Incomplete) => Ok(None),
            Ok(token) => {
                let body = self.parser.get_body(&token);
                let mut buf = BytesMut::with_capacity(body.0.len() + body.1.len());
                buf.extend_from_slice(body.0);
                buf.extend_from_slice(body.1);
                self.parser.consume(token.into());
                Ok(Some(Decoded::Frame(buf.freeze())))
            }
            Err(Error::Junk { token, kind }) => {
                self.parser.consume(token);
                Ok(Some(Decoded::Junk(kind)))
            }
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Decoded {
    Frame(Bytes),
    Junk(JunkKind),
}

#[cfg(test)]
mod tests {
    use std::collections::VecDeque;

    use super::*;
    use proptest::collection::vec;
    use proptest::prelude::*;
    use tokio_util::codec::{Decoder as _, Encoder as _};

    proptest! {
        #[test]
        fn test(
            first in vec(0x00u8..0xffu8, 1..1024),
            junk in vec(0x00u8..0xffu8, 1..1024),
            last in vec(0x00u8..0xffu8, 1..1024)
        ) {
            prop_assume!(!junk.windows(2).any(|s| s == STX));
            let mut buf = BytesMut::new();
            let mut encoder = Encoder::new();
            encoder.encode(first.as_slice(), &mut buf).unwrap();
            buf.extend_from_slice(&junk);
            encoder.encode(last.as_slice(), &mut buf).unwrap();
            let mut decoder = Decoder::new(VecDeque::with_capacity(4096));
            assert_eq!(decoder.decode(&mut buf).unwrap().unwrap(), Decoded::Frame(first.into()));
            assert_eq!(decoder.decode(&mut buf).unwrap().unwrap(), Decoded::Junk(JunkKind::InvalidStx));
            assert_eq!(decoder.decode(&mut buf).unwrap().unwrap(), Decoded::Frame(last.into()));
            assert_eq!(decoder.decode(&mut buf).unwrap(), None);
        }
    }
}