cue_lib 0.1.0

cuesheet library
Documentation
use super::{
  checksum::calc_upc_a_checksum,
  error::{UpcParseError, UpcParseErrorKind},
};
use crate::core::Digits;

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

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

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

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

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

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

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

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

  #[inline]
  pub fn left_part(&self) -> Digits<5> {
    unsafe { Digits::new_unchecked(&self.value[1..6]) }
  }

  #[inline]
  pub fn right_part(&self) -> Digits<5> {
    unsafe { Digits::new_unchecked(&self.value[6..11]) }
  }
}

impl core::str::FromStr for UpcA {
  type Err = UpcParseError;

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

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

    let upc = UpcA::new(value);

    if upc.checksum() == checksum {
      Ok(upc)
    } else {
      Err(UpcParseError::new(UpcParseErrorKind::ChecksumFail))
    }
  }
}

impl core::fmt::Display for UpcA {
  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 UpcA {
  fn cmp(&self, other: &Self) -> core::cmp::Ordering {
    self.value.cmp(&other.value)
  }
}

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

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