use crate::secondary_validation::Validator;
use std::collections::HashMap;
pub struct ItalianNationalIdChecksum;
const ITALIAN_NATIONAL_ID_LENGTH: usize = 16;
use lazy_static::lazy_static;
lazy_static! {
static ref ODD_CHARACTERS_MAPPING: HashMap<char, u32> = HashMap::from([
('0', 1),
('1', 0),
('2', 5),
('3', 7),
('4', 9),
('5', 13),
('6', 15),
('7', 17),
('8', 19),
('9', 21),
('A', 1),
('B', 0),
('C', 5),
('D', 7),
('E', 9),
('F', 13),
('G', 15),
('H', 17),
('I', 19),
('J', 21),
('K', 2),
('L', 4),
('M', 18),
('N', 20),
('O', 11),
('P', 3),
('Q', 6),
('R', 8),
('S', 12),
('T', 14),
('U', 16),
('V', 10),
('W', 22),
('X', 25),
('Y', 24),
('Z', 23),
]);
}
impl Validator for ItalianNationalIdChecksum {
fn is_valid_match(&self, regex_match: &str) -> bool {
let valid_chars = regex_match
.chars()
.filter(|c| ODD_CHARACTERS_MAPPING.contains_key(c));
let mut checksum_char = '0';
let mut checksum_value = 0;
for (idx, c) in valid_chars.enumerate() {
let position = idx + 1;
if position == ITALIAN_NATIONAL_ID_LENGTH {
checksum_char = c;
break;
}
if position % 2 == 1 {
checksum_value += ODD_CHARACTERS_MAPPING[&c];
} else {
if let Some(digit) = c.to_digit(10) {
checksum_value += digit
} else {
checksum_value += (c as u8 - b'A') as u32;
}
}
}
let computed_checksum_value = (b'A' + (checksum_value % 26) as u8) as char;
checksum_char == computed_checksum_value
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_valid_italian_national_id() {
let validator = ItalianNationalIdChecksum;
assert!(validator.is_valid_match("MRTMTT91D08 F205J"));
assert!(validator.is_valid_match("MLLSNT82P65-Z404U"));
assert!(validator.is_valid_match("LKJLDJ/00E20/D635F"));
}
#[test]
fn test_invalid_italian_national_id() {
let validator = ItalianNationalIdChecksum;
assert!(!validator.is_valid_match("MRTMTT91D08F205V"));
assert!(!validator.is_valid_match("MLLSNT82P65Z404T"));
assert!(!validator.is_valid_match("LKJLDJ00E20D635P"));
}
}