caff-archive 0.1.0

a library for manipulating CAFF archives
Documentation
use crate::prelude::*;

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(test, derive(Arbitrary))]
pub struct Padding<const N: usize>([u8; N]);

impl<const N: usize> From<[u8; N]> for Padding<N> {
  fn from(value: [u8; N]) -> Self {
    Self(value)
  }
}

impl<const N: usize> From<Padding<N>> for [u8; N] {
  fn from(value: Padding<N>) -> Self {
    value.into_inner()
  }
}

impl<const N: usize> Default for Padding<N> {
  fn default() -> Self {
    Self([0u8; N])
  }
}

impl<const N: usize> std::fmt::Debug for Padding<N> {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    if self.is_empty() {
      f.write_fmt(format_args!("Padding({} bytes) - EMPTY", N))
    } else if self.is_all_u8_max() {
      f.write_fmt(format_args!("Padding({} bytes) - ALL MAXES", N))
    } else {
      f.write_fmt(format_args!("Padding({} bytes) - NOT EMPTY - {:02X?}", N, self.0))
    }
  }
}

impl<const N: usize> Padding<N> {
  pub fn is_empty(&self) -> bool {
    self.0.iter().all(|value| value == &0)
  }

  pub fn is_all_u8_max(&self) -> bool {
    self.0.iter().all(|value| value == &u8::MAX)
  }

  pub fn into_inner(self) -> [u8; N] {
    self.0
  }

  pub fn read<R: Read + Seek>(reader: &mut R) -> Result<Self> {
    reader.trace(|reader| {
      let mut bytes = [0u8; N];
      reader.read_exact(&mut bytes)?;

      let value = Self(bytes);

      #[cfg(feature = "logging")]
      if !value.is_empty() {
        if value.is_all_u8_max() {
          log::debug!("read value={value:?}")
        } else {
          log::warn!("read value={value:?}")
        }
      }

      Ok(value)
    })
  }

  pub fn write<W: Write + Seek>(self, writer: &mut W) -> Result<()> {
    writer.trace(|writer| {
      writer.write_all(&self.0)?;
      Ok(())
    })
  }
}

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

  #[proptest]
  fn roundtrip(expected: Padding<4>) {
    let mut buffer = Cursor::new(Vec::new());

    expected.write(&mut buffer).unwrap();
    buffer.rewind().unwrap();
    let actual = Padding::read(&mut buffer).unwrap();

    assert_eq!(expected, actual);
  }
}