use std::fmt::{Display, Formatter};
use std::str::FromStr;
const FACTORS: [u8; 9] = [10, 9, 8, 7, 6, 5, 4, 3, 2];
#[derive(Debug)]
pub enum Error {
InvalidDigit(char),
InvalidLength(usize),
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidDigit(chr) => write!(f, "Invalid digit: {chr}"),
Self::InvalidLength(len) => write!(f, "Invalid length: {len}"),
}
}
}
#[derive(Debug)]
pub struct Isbn([u8; 10]);
impl Display for Isbn {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
for digit in self.0 {
if digit == 10 {
write!(f, "X")?;
} else {
write!(f, "{digit}")?;
}
}
Ok(())
}
}
impl From<[u8; 9]> for Isbn {
fn from(value: [u8; 9]) -> Self {
Self([
value[0],
value[1],
value[2],
value[3],
value[4],
value[5],
value[6],
value[7],
value[8],
calculate_checksum(&value),
])
}
}
impl FromStr for Isbn {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut digits = Vec::new();
for c in s.chars() {
if let Some(digit) = c.to_digit(10).and_then(|digit| u8::try_from(digit).ok()) {
digits.push(digit);
} else {
return Err(Error::InvalidDigit(c));
}
}
let array: [u8; 9] = match digits.try_into() {
Ok(array) => array,
Err(error) => return Err(Error::InvalidLength(error.len())),
};
Ok(Self::from(array))
}
}
fn calculate_checksum(digits: &[u8; 9]) -> u8 {
let dot_product: u32 = digits
.iter()
.zip(FACTORS)
.map(|(&x, y)| u32::from(x) * u32::from(y))
.sum();
for checksum in 0..=10u8 {
if (dot_product + u32::from(checksum)) % 11 == 0 {
return checksum;
}
}
0
}
use std::fmt::{Display, Formatter};
use std::str::FromStr;
const FACTORS: [u8; 9] = [10, 9, 8, 7, 6, 5, 4, 3, 2];
#[derive(Debug)]
pub enum Error {
InvalidDigit(char),
InvalidLength(usize),
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidDigit(chr) => write!(f, "Invalid digit: {chr}"),
Self::InvalidLength(len) => write!(f, "Invalid length: {len}"),
}
}
}
#[derive(Debug)]
pub struct Isbn([u8; 9]);
impl Isbn {
#[must_use]
pub fn checksum(&self) -> u8 {
let dot_product: u32 = self
.0
.iter()
.zip(FACTORS)
.map(|(&x, y)| u32::from(x) * u32::from(y))
.sum();
for checksum in 0..=10u8 {
if (dot_product + u32::from(checksum)) % 11 == 0 {
return checksum;
}
}
0
}
}
impl Display for Isbn {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
for digit in self.0 {
write!(f, "{digit}")?;
}
match self.checksum() {
10 => write!(f, "X"),
checksum => write!(f, "{checksum}"),
}
}
}
impl From<[u8; 9]> for Isbn {
fn from(value: [u8; 9]) -> Self {
Self(value)
}
}
impl FromStr for Isbn {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut digits = Vec::new();
for c in s.chars() {
if let Some(digit) = c.to_digit(10).and_then(|digit| u8::try_from(digit).ok()) {
digits.push(digit);
} else {
return Err(Error::InvalidDigit(c));
}
}
let array: [u8; 9] = match digits.try_into() {
Ok(array) => array,
Err(error) => return Err(Error::InvalidLength(error.len())),
};
Ok(Self::from(array))
}
}
use std::fmt::{Display, Formatter};
use std::str::FromStr;
const FACTORS: [u8; 9] = [10, 9, 8, 7, 6, 5, 4, 3, 2];
#[derive(Debug)]
pub enum Error {
InvalidDigit(char),
InvalidLength(usize),
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidDigit(chr) => write!(f, "Invalid digit: {chr}"),
Self::InvalidLength(len) => write!(f, "Invalid length: {len}"),
}
}
}
#[derive(Debug)]
pub struct Isbn(u32);
impl Isbn {
#[must_use]
pub fn digits(&self) -> String {
self.0.to_string()
}
#[must_use]
pub fn checksum(&self) -> u8 {
let dot_product: u32 = self
.digits()
.chars()
.map(|c| c.to_digit(10).unwrap_or_else(|| unreachable!()))
.zip(FACTORS)
.map(|(x, y)| x * u32::from(y))
.sum();
for checksum in 0..=10u8 {
if (dot_product + u32::from(checksum)) % 11 == 0 {
return checksum;
}
}
0
}
fn from_digits(digits: &[u8]) -> Result<Self, Error> {
if digits.len() != 9 {
return Err(Error::InvalidLength(digits.len()));
}
Ok(Self(
digits
.iter()
.rev()
.enumerate()
.map(|(exponent, digit)| {
u32::from(*digit)
* 10u32.pow(u32::try_from(exponent).unwrap_or_else(|_| unreachable!()))
})
.sum(),
))
}
}
impl Display for Isbn {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.digits())?;
match self.checksum() {
10 => write!(f, "X"),
checksum => write!(f, "{checksum}"),
}
}
}
impl FromStr for Isbn {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut digits = Vec::new();
for c in s.chars() {
if let Some(digit) = c.to_digit(10).and_then(|digit| u8::try_from(digit).ok()) {
digits.push(digit);
} else {
return Err(Error::InvalidDigit(c));
}
}
Self::from_digits(digits.as_slice())
}
}