cue_lib 0.1.0

cuesheet library
Documentation
use super::{
  checksum::calc_ean_13_checksum,
  error::{EanParseError, EanParseErrorKind},
};
use crate::core::Digits;

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Ean13 {
  value: Digits<13>,
}

impl Ean13 {
  pub fn new(code: Digits<12>) -> Self {
    let checksum = calc_ean_13_checksum(&code);
    let mut value = [0u8; 13];

    (&mut value[..12]).copy_from_slice(code.as_bytes());
    value[12] = checksum;

    Self {
      value: unsafe { Digits::new_unchecked(&value) },
    }
  }

  #[inline]
  pub fn as_ascii_bytes(&self) -> [u8; 13] {
    self.value.as_ascii_bytes()
  }

  #[inline]
  pub const fn as_bytes(&self) -> &[u8; 13] {
    self.value.as_bytes()
  }

  #[inline]
  pub fn gs1(&self) -> Digits<3> {
    unsafe {
      Digits::new_unchecked(
        self.value.as_bytes()[..3]
          .try_into()
          .expect("gs1 never panics"),
      )
    }
  }

  #[inline]
  pub fn checksum(&self) -> u8 {
    self.value[12]
  }

  #[inline]
  pub fn code(&self) -> Digits<9> {
    unsafe {
      Digits::new_unchecked(
        self.value.as_bytes()[3..]
          .try_into()
          .expect("variable code never panics"),
      )
    }
  }
}

impl core::str::FromStr for Ean13 {
  type Err = EanParseError;

  fn from_str(s: &str) -> Result<Self, Self::Err> {
    if s.len() != 13 {
      return Err(EanParseError::new(EanParseErrorKind::InvalidLength));
    }

    let value = Digits::<12>::from_str(&s[..12])
      .map_err(|_| EanParseError::new(EanParseErrorKind::InvalidCharacter))?;
    let checksum = match s.as_bytes().last() {
      Some(value @ b'0'..=b'9') => Ok(value - b'0'),
      _ => Err(EanParseError::new(EanParseErrorKind::InvalidCharacter)),
    }?;

    let ean_code = Ean13::new(value);

    if ean_code.checksum() == checksum {
      Ok(ean_code)
    } else {
      Err(EanParseError::new(EanParseErrorKind::ChecksumFail))
    }
  }
}

impl core::fmt::Display for Ean13 {
  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
    let bytes = self.as_ascii_bytes();
    let ascii_text = unsafe { core::str::from_utf8_unchecked(&bytes) };
    f.write_str(ascii_text)
  }
}

impl Ord for Ean13 {
  #[inline]
  fn cmp(&self, other: &Self) -> core::cmp::Ordering {
    self.value.cmp(&other.value)
  }
}

impl PartialOrd for Ean13 {
  #[inline]
  fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
    Some(self.cmp(other))
  }
}

impl From<Digits<12>> for Ean13 {
  #[inline]
  fn from(value: Digits<12>) -> Self {
    Self::new(value)
  }
}