jubako 0.4.0

The reference implementation of the Jubako container format
Documentation
use crate::bases::*;
use std::{
    borrow::{Borrow, Cow},
    marker::PhantomData,
};

pub struct PArray<Output> {
    _output: PhantomData<Output>,
}

impl<Output> PArray<Output> {
    fn serialize_string_size<T>(string: &T, max_len: u8, ser: &mut Serializer) -> IoResult<usize>
    where
        Output: Borrow<T>,
        T: AsRef<[u8]> + ?Sized,
    {
        let string = string.as_ref();
        assert!(string.len() <= max_len.into());
        let mut written = 0;
        written += ser.write_u8(string.len() as u8)?;
        written += ser.write_data(string)?;
        Ok(written)
    }
    pub(crate) fn serialize_string<T>(string: &T, ser: &mut Serializer) -> IoResult<usize>
    where
        Output: Borrow<T>,
        T: AsRef<[u8]> + ?Sized,
    {
        Self::serialize_string_size(string, 255, ser)
    }

    pub(crate) fn serialize_string_padded<T>(
        string: &T,
        size: u8,
        ser: &mut Serializer,
    ) -> IoResult<usize>
    where
        Output: Borrow<T>,
        T: AsRef<[u8]> + ?Sized,
    {
        let mut written = 0;
        written += Self::serialize_string_size(string, size, ser)?;
        written += ser.write_data(vec![0; size as usize - string.as_ref().len()].as_slice())?;
        Ok(written)
    }
}

impl Parsable for PArray<SmallBytes> {
    type Output = SmallBytes;
    fn parse(parser: &mut impl Parser) -> Result<SmallBytes> {
        let size = parser.read_u8()?;
        match parser.read_slice(size as usize)? {
            Cow::Borrowed(slice) => Ok(slice.into()),
            Cow::Owned(vec) => Ok(vec.into()),
        }
    }
}

impl Parsable for PArray<String> {
    type Output = SmallString;
    fn parse(parser: &mut impl Parser) -> Result<SmallString> {
        let data = PBytes::parse(parser)?;
        Ok(SmallString::from_byte_vec(data)?)
    }
}

pub type PBytes = PArray<SmallBytes>;
pub type PString = PArray<String>;

#[cfg(test)]
mod tests {
    use super::*;

    #[derive(Clone)]
    pub struct TestPString(&'static [u8], &'static str);
    impl rustest::ParamName for TestPString {
        fn param_name(&self) -> String {
            self.1.into()
        }
    }

    #[rustest::test(params:TestPString = [
        TestPString(&[0x00_u8] as &[u8] , ""),
        TestPString(&[0x01, 72] , "H"),
        TestPString(&[0x02, 72, 101],"He"),
        TestPString(&[0x03, 72, 0xC3, 0xA9], "")
    ])]
    fn test_pstring(Param(TestPString(source, expected)): Param) {
        let mut content = Vec::new();
        content.extend_from_slice(source);
        let reader = CheckReader::from(content);
        let parsed = reader
            .parse_in::<PString>(Offset::zero(), reader.size().try_into().unwrap())
            .unwrap()
            .to_string();
        assert_eq!(parsed, expected);
    }

    #[derive(Clone)]
    pub struct TestPVec(&'static [u8], &'static [u8]);
    impl rustest::ParamName for TestPVec {
        fn param_name(&self) -> String {
            format!("{:?}", self.1)
        }
    }
    #[rustest::test(params:TestPVec = [
        TestPVec(&[0x00_u8] as &[u8] , b""),
        TestPVec(&[0x01, 72] ,  b"H"),
        TestPVec(&[0x02, 72, 101], b"He"),
        TestPVec(&[0x03, 72, 0xC3, 0xA9], "".as_bytes())
    ])]
    fn test_pvec(Param(TestPVec(source, expected)): Param) {
        let mut content = Vec::new();
        content.extend_from_slice(source);
        let reader = CheckReader::from(content);
        let parsed = reader
            .parse_in::<PBytes>(Offset::zero(), reader.size().try_into().unwrap())
            .unwrap()
            .to_vec();
        assert_eq!(parsed, expected);
    }
}