use std::{num::ParseIntError, ops::RangeInclusive, str::FromStr};
use thiserror::Error;
use crate::aux::Stack;
#[derive(Stack!)]
pub struct Gtin(u64);
impl Gtin {
pub const DIGITS_MIN: u8 = 8;
pub const DIGITS_MAX: u8 = 14;
pub const DIGITS_RANGE: RangeInclusive<u8> = Self::DIGITS_MIN..=Self::DIGITS_MAX;
pub const MAX: Self = Self(10u64.pow(Self::DIGITS_MAX as u32) - 1);
pub fn new(source: u64) -> Result<Self, OutOfRangeError> {
let gtin = Self(source);
if gtin.digits() > Self::DIGITS_MAX {
return Err(OutOfRangeError {
orig: source,
n: gtin.digits(),
});
}
Ok(gtin)
}
#[must_use]
pub fn get(&self) -> u64 {
self.0
}
#[must_use]
pub fn digits(&self) -> u8 {
let n = self.0;
if n == 0 { 1 } else { n.ilog10() as u8 + 1 }
}
}
impl FromStr for Gtin {
type Err = GtinParseError;
fn from_str(source: &str) -> Result<Self, Self::Err> {
let parsed = source.parse()?;
let digits = source.len() as u8;
if !Self::DIGITS_RANGE.contains(&digits) {
return Err(OutOfRangeError {
orig: parsed,
n: digits,
}
.into());
}
let gtin = Self::new(parsed)?;
Ok(gtin)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum GtinParseError {
#[error("couldn't parse as an integer: {0}")]
ExpectedInteger(#[from] ParseIntError),
#[error("valid int, but out of range: {0}")]
OutOfRange(#[from] OutOfRangeError),
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error(
"`{orig}` contains {n} digits, which is not in the allowed range [{}, {}]",
Gtin::DIGITS_MIN,
Gtin::DIGITS_MAX
)]
pub struct OutOfRangeError {
pub orig: u64,
pub n: u8,
}