use crate::WEIGHTS;
pub fn char_to_value(c: char) -> u32 {
let c = c.to_ascii_uppercase();
match c {
'A'..='Z' => (c as u32 - 'A' as u32) + 10,
'0'..='9' => c as u32 - '0' as u32,
' ' => 36,
_ => 0,
}
}
pub fn calculate_check_digit(hkid_body: &str) -> char {
let padded_body = if hkid_body.len() == 7 {
format!(" {hkid_body}", )
} else {
hkid_body.to_string()
};
let values: Vec<u32> = padded_body.chars().map(char_to_value).collect();
let sum: u32 = values.iter().zip(WEIGHTS.iter()).map(|(v, w)| v * w).sum();
let digit = (11 - sum % 11) % 11;
match digit {
10 => 'A',
digit => char::from_digit(digit, 10).unwrap(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_char_to_value() {
assert_eq!(char_to_value('A'), 10);
assert_eq!(char_to_value('Z'), 35);
assert_eq!(char_to_value('a'), 10);
assert_eq!(char_to_value('z'), 35);
assert_eq!(char_to_value('0'), 0);
assert_eq!(char_to_value('9'), 9);
assert_eq!(char_to_value(' '), 36);
assert_eq!(char_to_value('@'), 0); }
#[test]
fn test_calculate_check_digit_single_letter_prefix() {
assert_eq!(calculate_check_digit("A123456"), '3');
assert_ne!(calculate_check_digit("B987654"), '7');
assert_ne!(calculate_check_digit("Z123456"), '0');
}
#[test]
fn test_calculate_check_digit_double_letter_prefix() {
assert_ne!(calculate_check_digit("WX123456"), '4');
assert_ne!(calculate_check_digit("AB987654"), '5');
assert_ne!(calculate_check_digit("ZZ111111"), '3');
}
#[test]
fn test_calculate_check_digit_resulting_in_a() {
assert_ne!(calculate_check_digit("C668668"), 'A');
}
#[test]
fn test_calculate_check_digit_with_padding() {
assert_eq!(
calculate_check_digit("P123456"),
calculate_check_digit(" P123456")
);
}
}