pub fn is_valid_cnh(cnh: &str) -> bool {
let cnh_digits: String = cnh.chars().filter(|c| c.is_ascii_digit()).collect();
if cnh_digits.is_empty() {
return false;
}
if cnh_digits.len() != 11 {
return false;
}
if cnh_digits
.chars()
.all(|c| c == cnh_digits.chars().next().unwrap())
{
return false;
}
let digits: Vec<u32> = cnh_digits
.chars()
.map(|c| c.to_digit(10).unwrap())
.collect();
let first_verificator = digits[9];
let second_verificator = digits[10];
if !check_first_verificator(&digits, first_verificator) {
return false;
}
check_second_verificator(&digits, second_verificator, first_verificator)
}
fn check_first_verificator(digits: &[u32], first_verificator: u32) -> bool {
let mut sum = 0;
for (i, &digit) in digits.iter().enumerate().take(9) {
sum += digit * (9 - i as u32);
}
let remainder = sum % 11;
let result = if remainder > 9 { 0 } else { remainder };
result == first_verificator
}
fn check_second_verificator(
digits: &[u32],
second_verificator: u32,
first_verificator: u32,
) -> bool {
let mut sum = 0;
for (i, &digit) in digits.iter().enumerate().take(9) {
sum += digit * (i as u32 + 1);
}
let mut result = sum % 11;
if first_verificator > 9 {
result = if (result as i32 - 2) < 0 {
result + 9
} else {
result - 2
};
}
if result > 9 {
result = 0;
}
result == second_verificator
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_valid_cnh() {
assert!(!is_valid_cnh("22222222222"));
assert!(!is_valid_cnh("00000000000"));
assert!(!is_valid_cnh("11111111111"));
assert!(!is_valid_cnh("33333333333"));
assert!(!is_valid_cnh("99999999999"));
assert!(!is_valid_cnh("ABC70304734"));
assert!(!is_valid_cnh("A2C45678901"));
assert!(!is_valid_cnh("1234567890A"));
assert!(!is_valid_cnh("6619558737912"));
assert!(!is_valid_cnh("123456789"));
assert!(!is_valid_cnh("1234567890"));
assert!(!is_valid_cnh("123456789012"));
assert!(is_valid_cnh("097703047-34"));
assert!(is_valid_cnh("987654321-00"));
assert!(is_valid_cnh("09770304734"));
assert!(is_valid_cnh("98765432100"));
assert!(!is_valid_cnh("12345678901"));
assert!(!is_valid_cnh(""));
assert!(!is_valid_cnh(" "));
assert!(!is_valid_cnh("---"));
}
#[test]
fn test_check_first_verificator() {
let digits = vec![0, 9, 7, 7, 0, 3, 0, 4, 7, 3, 4];
assert!(check_first_verificator(&digits, 3));
assert!(!check_first_verificator(&digits, 5));
}
#[test]
fn test_check_second_verificator() {
let digits = vec![0, 9, 7, 7, 0, 3, 0, 4, 7, 3, 4];
assert!(check_second_verificator(&digits, 4, 3));
assert!(!check_second_verificator(&digits, 5, 3));
}
#[test]
fn test_is_valid_cnh_symbols_removed() {
assert!(is_valid_cnh("097-703-047-34"));
assert!(is_valid_cnh("097.703.047.34"));
assert!(is_valid_cnh("097 703 047 34"));
assert!(is_valid_cnh("(097)703-047-34"));
}
#[test]
fn test_is_valid_cnh_mixed_invalid() {
assert!(!is_valid_cnh("0977O3O4734")); assert!(!is_valid_cnh("097703O4734"));
}
#[test]
fn test_edge_cases_first_verificator_greater_than_9() {
let digits = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0];
let _ = check_second_verificator(&digits, 0, 10);
}
}