cesrox 0.1.6

Implementation of Composable Event Streaming Representation (CESR)
Documentation
use std::str::FromStr;

use crate::{
    conversion::{adjust_with_num, b64_to_num},
    derivation_code::DerivationCode,
    error::Error,
};

#[derive(Debug, PartialEq, Eq)]
pub enum MaterialPathCode {
    ZeroLeadBytes(u16),
    OneLeadBytes(u16),
    TwoLeadBytes(u16),
}

impl DerivationCode for MaterialPathCode {
    fn soft_size(&self) -> usize {
        2
    }

    fn hard_size(&self) -> usize {
        2
    }

    fn value_size(&self) -> usize {
        0
    }

    fn to_str(&self) -> String {
        let (code, data_len) = match self {
            MaterialPathCode::ZeroLeadBytes(data_lenght) => ("4A", data_lenght),
            MaterialPathCode::OneLeadBytes(data_length) => ("5A", data_length),
            MaterialPathCode::TwoLeadBytes(data_length) => ("6A", data_length),
        };
        let data = adjust_with_num(data_len.to_owned(), self.soft_size());
        [code, &data].join("")
    }
}

impl MaterialPathCode {
    pub fn size(&self) -> u16 {
        match self {
            MaterialPathCode::ZeroLeadBytes(n) => n,
            MaterialPathCode::OneLeadBytes(n) => n,
            MaterialPathCode::TwoLeadBytes(n) => n,
        }
        .to_owned()
    }

    pub fn lead_bytes_len(&self) -> usize {
        match self {
            MaterialPathCode::ZeroLeadBytes(_) => 0,
            MaterialPathCode::OneLeadBytes(_) => 1,
            MaterialPathCode::TwoLeadBytes(_) => 2,
        }
    }

    pub fn new(lead_length: usize, data_len: u16) -> Result<Self, Error> {
        match lead_length {
            0 => Ok(Self::ZeroLeadBytes(data_len)),
            1 => Ok(Self::OneLeadBytes(data_len)),
            2 => Ok(Self::TwoLeadBytes(data_len)),
            _ => Err(Error::IncorrectLengthError(
                "Wrong lead bytes length".into(),
            )),
        }
    }
}

impl FromStr for MaterialPathCode {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let code = s.get(..2).ok_or(Error::EmptyCodeError)?;
        let count_part = s.get(2..4).ok_or(Error::EmptyCodeError)?;
        let count = b64_to_num(count_part.as_bytes())?;
        match code {
            "4A" => Ok(Self::ZeroLeadBytes(count)),
            "5A" => Ok(Self::OneLeadBytes(count)),
            "6A" => Ok(Self::TwoLeadBytes(count)),
            _ => Err(Error::UnknownCodeError),
        }
    }
}

#[test]
pub fn test_material_path_code_to_str() -> Result<(), Error> {
    assert_eq!(MaterialPathCode::new(0, 1)?.to_str(), "4AAB".to_string());
    assert_eq!(MaterialPathCode::new(1, 100)?.to_str(), "5ABk".to_string());
    assert_eq!(MaterialPathCode::new(2, 64)?.to_str(), "6ABA".to_string());
    assert!(MaterialPathCode::new(3, 64).is_err());

    Ok(())
}

#[test]
pub fn test_material_path_code_from_str() -> Result<(), Error> {
    assert_eq!(MaterialPathCode::new(0, 1)?, "4AAB".parse()?);
    assert_eq!(MaterialPathCode::new(1, 100)?, "5ABk".parse()?);
    assert_eq!(MaterialPathCode::new(2, 64)?, "6ABA".parse()?);
    assert_eq!(
        Err(Error::UnknownCodeError),
        "AAAA".parse::<MaterialPathCode>()
    );

    Ok(())
}