Skip to content

Instantly share code, notes, and snippets.

@fancellu
Last active February 15, 2024 11:20
Show Gist options
  • Save fancellu/e9e4ada514572dfddd3c3063528d426f to your computer and use it in GitHub Desktop.
Save fancellu/e9e4ada514572dfddd3c3063528d426f to your computer and use it in GitHub Desktop.
Rust check digit calculation for ISBN-13
use crate::InvalidIsbn::{BadChecksum, TooLong, TooShort};
use std::str::FromStr;
// check digit calculation for ISBN-13
#[derive(Debug)]
struct Isbn {
raw: String,
}
#[derive(Debug)]
enum InvalidIsbn {
TooLong,
TooShort,
BadChecksum,
}
fn sum_string_functional(str: &str) -> u32 {
str.chars().map(|c| c.to_digit(10).unwrap()).sum()
}
fn to_result(isbn: &str) -> Result<Isbn, InvalidIsbn> {
let no_hyphens = isbn.replace("-", "");
if no_hyphens.len() != 13 {
return Err(if no_hyphens.len() < 13 {
TooShort
} else {
TooLong
});
}
let (first, checkdigit) = no_hyphens.split_at(12);
let even_chars: String = first
.chars()
.enumerate()
.filter(|(i, _)| i % 2 == 0)
.map(|(_, c)| c)
.collect();
let even_sum = sum_string_functional(even_chars.as_str());
let odd_chars: String = first
.chars()
.enumerate()
.filter(|(i, _)| i % 2 != 0)
.map(|(_, c)| c)
.collect();
let odd_sum = sum_string_functional(odd_chars.as_str()) * 3;
let modulo = ((even_sum + odd_sum) % 10) as u8;
let actual = (10 - modulo) % 10;
let expected = checkdigit.chars().next().unwrap().to_digit(10).unwrap() as u8;
if expected == actual {
Ok(Isbn {
raw: isbn.to_string(),
})
} else {
Err(BadChecksum)
}
}
// Remove hyphens from ISBN
impl FromStr for Isbn {
type Err = InvalidIsbn;
fn from_str(s: &str) -> Result<Self, Self::Err> {
to_result(s)
}
}
impl std::fmt::Display for Isbn {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "ISBN: {}", self.raw)
}
}
fn main() {
let isbn_obj: Isbn = "978-0-306-40615-7".parse().unwrap();
println!("{}", isbn_obj);
let isbn_bad: Result<Isbn, _> = "978-0-306-40615-2".parse();
println!("{:?}", isbn_bad);
let isbn_bad: Result<Isbn, _> = "978-0-305-7".parse();
println!("{:?}", isbn_bad);
let isbn_bad: Result<Isbn, _> = "978-0-306-40615-7-1234".parse();
println!("{:?}", isbn_bad);
}
@fancellu
Copy link
Author

fancellu commented Feb 7, 2024

Output

ISBN: 978-0-306-40615-7
Err(BadChecksum)
Err(TooShort)
Err(TooLong)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment