open-protocol-codec 0.1.0

Helper library with encoding and decoding functionalities for Atlas Copco/Torque Open Protocol.
Documentation
use chrono::{DateTime, Datelike, Local, Timelike};
use thiserror;
use crate::{FieldNumber, FIELD_NUMBER_LEN};

pub trait Encode {
    fn encode(&self, encoder: &mut Encoder) -> Result<()>;

    fn encode_sized(&self , encoder: &mut Encoder, _size: usize) -> Result<()> {
        self.encode(encoder)
    }
}

#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum Error {
    #[error("This feature is not implemented yet")]
    NotImplemented,
    #[error("Cannot write this as {0} characters")]
    InvalidSize(usize),
    #[error("Unsized encode is not allowed for this type.")]
    UnsizedEncodeNotAllowed,
    #[error("Character is a non-ASCII character, which is required.")]
    NonAsciiCharacter(char)
}

pub type Result<T> = std::result::Result<T, Error>;


#[derive(Debug, Clone)]
pub struct Encoder {
    bytes: Vec<u8>,
}

impl Encoder {
    pub fn new() -> Self {
        Self { bytes: vec![] }
    }

    pub fn write_byte(&mut self, byte: &u8) -> Result<()> {
        self.bytes.push(*byte);
        Ok(())
    }

    pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<()> {
        for byte_char in bytes {
            self.write_byte(byte_char)?;
        }
        Ok(())
    }

    pub fn write_sized_field<T: Encode>(&mut self, item: &T, size: usize) -> Result<()> {
        item.encode_sized(self, size)?;
        Ok(())
    }

    pub fn write_numbered_sized_field<T: Encode>(&mut self, item: &T, number: FieldNumber, size: usize) -> Result<()> {
        number.encode_sized(self, FIELD_NUMBER_LEN)?;
        item.encode_sized(self, size)?;
        Ok(())
    }

    pub fn write_numbered_sized_optional_field<T: Encode>(&mut self, item: &Option<T>, number: FieldNumber, size: usize) -> Result<()> {
        match item {
            Some(content) => {
                number.encode_sized(self, FIELD_NUMBER_LEN)?;
                content.encode_sized(self, size)?;
            },
            None => {},
        };
        Ok(())
    }

    pub fn write_numbered_field<T: Encode>(&mut self, item: &T, number: FieldNumber) -> Result<()> {
        number.encode_sized(self, FIELD_NUMBER_LEN)?;
        item.encode(self)?;
        Ok(())
    }

    pub fn write_numbered_optional_field<T: Encode>(&mut self, item: &Option<T>, number: FieldNumber) -> Result<()> {
        match item {
            Some(content) => {
                number.encode_sized(self, FIELD_NUMBER_LEN)?;
                content.encode(self)?;
            },
            None => {},
        }
        Ok(())
    }

    pub fn write_sized_list<T: Encode>(&mut self, list: &Vec<T>, item_size: usize, amount: usize) -> Result<()> {
        for index in 0..amount {
            let item = &list[index];
            self.write_sized_field(item, item_size)?;
        }

        Ok(())
    }

    pub fn write_list<T: Encode>(&mut self, list: &Vec<T>, amount: usize) -> Result<()> {
        for index in 0..amount {
            let item = &list[index];
            item.encode(self)?
        }

        Ok(())
    }

    pub fn to_string(&self) -> String {
        let bytes = self.bytes.clone();
        String::from_utf8(bytes).unwrap()
    }

    pub fn len(&self) -> usize {
        self.bytes.len()
    }

    pub fn as_slice(&self) -> &[u8] {
        self.bytes.as_slice()
    }

    pub fn append(&mut self, bytes: &[u8]) {
        self.bytes.extend_from_slice(bytes);
    }
}

pub fn encode<T: Encode>(item: &T) -> Result<String> {
    let mut encoder = Encoder::new();
    item.encode(&mut encoder)?;
    Ok(encoder.to_string())
}

impl Encode for u8 {
    fn encode(&self, _: &mut Encoder) -> Result<()> {
        Err(Error::UnsizedEncodeNotAllowed)
    }

    fn encode_sized(&self, encoder: &mut Encoder, size: usize) -> Result<()> {
        for i in (0..size).rev() {
            let digit_byte = (self / 10u8.pow(i as u32)) % 10 + b'0';
            encoder.write_byte(&digit_byte)?;
        }
        Ok(())
    }
}

impl Encode for u16 {
    fn encode(&self, _: &mut Encoder) -> Result<()> {
        Err(Error::UnsizedEncodeNotAllowed)
    }

    fn encode_sized(&self, encoder: &mut Encoder, size: usize) -> Result<()> {
        for i in (0..size).rev() {
            let digit_byte = ((self / 10u16.pow(i as u32)) % 10) as u8 + b'0';
            encoder.write_byte(&digit_byte)?;
        }

        Ok(())
    }
}

impl Encode for u32 {
    fn encode(&self, _: &mut Encoder) -> Result<()> {
        Err(Error::UnsizedEncodeNotAllowed)
    }

    fn encode_sized(&self, encoder: &mut Encoder, size: usize) -> Result<()> {
        for i in (0..size).rev() {
            let digit_byte = ((self / 10u32.pow(i as u32)) % 10) as u8 + b'0';
            encoder.write_byte(&digit_byte)?;
        }

        Ok(())
    }
}

impl Encode for u64 {
    fn encode(&self, _: &mut Encoder) -> Result<()> {
        Err(Error::UnsizedEncodeNotAllowed)
    }

    fn encode_sized(&self, encoder: &mut Encoder, size: usize) -> Result<()> {
        for i in (0..size).rev() {
            let digit_byte = ((self / 10u64.pow(i as u32)) % 10) as u8 + b'0';
            encoder.write_byte(&digit_byte)?;
        }

        Ok(())
    }
}

impl Encode for char {
    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
        if !self.is_ascii() {
            return Err(Error::NonAsciiCharacter(*self));
        }

        encoder.write_byte(&u8::try_from(u32::from(*self)).unwrap())?;
        Ok(())
    }
}

impl Encode for String {
    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
        for char in self.chars() {
            char.encode(encoder)?;
        }

        Ok(())
    }

    fn encode_sized(&self, encoder: &mut Encoder, size: usize) -> Result<()> {
        if self.len() > size {
            return Err(Error::InvalidSize(size));
        }

        for char in self.chars() {
            char.encode(encoder)?;
        }

        for _ in 0..(size - self.len()) {
            ' '.encode(encoder)?;
        }

        Ok(())
    }
}

impl Encode for bool {
    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
        match *self {
            true => '1'.encode(encoder),
            false => '0'.encode(encoder),
        }
    }

    fn encode_sized(&self, encoder: &mut Encoder, size: usize) -> Result<()> {
        if size != 1 {
            return Err(Error::InvalidSize(size));
        }

        self.encode(encoder)
    }
}

impl<T: Encode> Encode for Option<T> {
    fn encode(&self, _: &mut Encoder) -> Result<()> {
        Err(Error::UnsizedEncodeNotAllowed)
    }

    fn encode_sized(&self, encoder: &mut Encoder, size: usize) -> Result<()> {
        match self {
            None => {
                for char in std::iter::repeat(' ').take(size) {
                    char.encode(encoder)?;
                }
            },
            Some(item) => {
                item.encode_sized(encoder, size)?
            },
        };

        Ok(())
    }
}

impl Encode for DateTime<Local> {
    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
        (self.year() as u32).encode_sized(encoder, 4)?;
        '-'.encode(encoder)?;
        self.month().encode_sized(encoder, 2)?;
        '-'.encode(encoder)?;
        self.day().encode_sized(encoder, 2)?;
        ':'.encode(encoder)?;
        self.hour().encode_sized(encoder, 2)?;
        ':'.encode(encoder)?;
        self.minute().encode_sized(encoder, 2)?;
        ':'.encode(encoder)?;
        self.second().encode_sized(encoder, 2)?;

        Ok(())
    }
}