use rand::Rng;
const WEIGHTS: [u32; 10] = [2, 3, 4, 5, 6, 7, 8, 9, 2, 3];
pub fn calculate_checksum(base_renavam: &str) -> u32 {
if base_renavam.len() != 10 {
return 0;
}
let digits: Vec<u32> = base_renavam
.chars()
.rev()
.filter_map(|c| c.to_digit(10))
.collect();
if digits.len() != 10 {
return 0;
}
let sum: u32 = digits
.iter()
.zip(WEIGHTS.iter())
.map(|(digit, weight)| digit * weight)
.sum();
let remainder = sum % 11;
let check_digit = 11 - remainder;
if check_digit >= 10 {
0
} else {
check_digit
}
}
pub fn is_valid(renavam: &str) -> bool {
if renavam.len() != 11 {
return false;
}
if !renavam.chars().all(|c| c.is_ascii_digit()) {
return false;
}
let first_char = renavam.chars().next().unwrap();
if renavam.chars().all(|c| c == first_char) {
return false;
}
let base = &renavam[..10];
let check_digit = renavam.chars().nth(10).and_then(|c| c.to_digit(10));
if let Some(digit) = check_digit {
calculate_checksum(base) == digit
} else {
false
}
}
pub fn generate() -> String {
let mut rng = rand::thread_rng();
loop {
let base: String = (0..10).map(|_| rng.gen_range(0..10).to_string()).collect();
let first_char = base.chars().next().unwrap();
if base.chars().all(|c| c == first_char) {
continue;
}
let check_digit = calculate_checksum(&base);
return format!("{}{}", base, check_digit);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_calculate_checksum() {
assert_eq!(calculate_checksum("8676959730"), 8);
assert_eq!(calculate_checksum("0123456789"), 7);
assert_eq!(calculate_checksum("9876543210"), 3);
}
#[test]
fn test_is_valid() {
assert!(is_valid("86769597308"));
assert!(is_valid("01234567897"));
assert!(is_valid("98765432103"));
assert!(!is_valid("86769597309"));
assert!(!is_valid("12345678901"));
assert!(!is_valid("11111111111"));
assert!(!is_valid("00000000000"));
assert!(!is_valid("123"));
assert!(!is_valid("123456789012"));
assert!(!is_valid("1234567890a"));
assert!(!is_valid("12345678 01"));
assert!(!is_valid(""));
}
#[test]
fn test_generate() {
let renavam = generate();
assert_eq!(renavam.len(), 11);
assert!(is_valid(&renavam));
}
#[test]
fn test_generate_uniqueness() {
let mut renavams = std::collections::HashSet::new();
for _ in 0..100 {
let renavam = generate();
assert!(is_valid(&renavam));
renavams.insert(renavam);
}
assert!(renavams.len() >= 95);
}
#[test]
fn test_checksum_invalid_input() {
assert_eq!(calculate_checksum("123"), 0);
assert_eq!(calculate_checksum("12345678901"), 0);
assert_eq!(calculate_checksum(""), 0);
}
}