use crate::secondary_validation::Validator;
pub struct FranceSsnChecksum;
const FRANCE_SSN_LENGTH: usize = 15;
const FRANCE_SSN_CONTROL_KEY_LENGTH: usize = 2;
const FRANCE_SSN_FIRST_VALUE_LENGTH: usize = FRANCE_SSN_LENGTH - FRANCE_SSN_CONTROL_KEY_LENGTH;
const FRANCE_SSN_CHECKSUM_MODULUS: i64 = 97;
impl Validator for FranceSsnChecksum {
fn is_valid_match(&self, regex_match: &str) -> bool {
let mut valid_chars = regex_match
.chars()
.filter(|c| c.is_ascii_digit() || matches!(c, 'a' | 'A' | 'b' | 'B'));
if valid_chars.clone().count() != FRANCE_SSN_LENGTH {
return false;
}
let mut first_value: i64 = 0;
let mut penalties: i64 = 0;
for c in valid_chars.by_ref().take(FRANCE_SSN_FIRST_VALUE_LENGTH) {
if let Some(digit) = c.to_digit(10) {
first_value = first_value * 10 + digit as i64;
} else {
first_value *= 10; penalties += 1000000;
if matches!(c, 'b' | 'B') {
penalties += 1000000;
}
}
}
let mut control_key: i64 = 0;
for c in valid_chars.by_ref().take(FRANCE_SSN_CONTROL_KEY_LENGTH) {
if let Some(digit) = c.to_digit(10) {
control_key = control_key * 10 + digit as i64;
} else {
return false;
}
}
let final_value = first_value - penalties;
(FRANCE_SSN_CHECKSUM_MODULUS - final_value % FRANCE_SSN_CHECKSUM_MODULUS) == control_key
}
}
#[cfg(test)]
mod test {
use crate::secondary_validation::*;
#[test]
fn test_valid_france_ssn() {
let valid_ids = vec![
"2-89-04-78342-163-49",
"289047834221297",
"2 89 04 78342 211 01",
"289042A34216390",
"289042B34216320",
];
for id in valid_ids {
assert!(FranceSsnChecksum.is_valid_match(id));
}
}
#[test]
fn test_invalid_france_ssn() {
let invalid_ids = vec![
"278056933908923",
"278056933908997",
"289042A3421639A",
"2780569339089977",
"27805693390899",
];
for id in invalid_ids {
assert!(!FranceSsnChecksum.is_valid_match(id));
}
}
}