use std::borrow::Cow;
use std::collections::HashSet;
use once_cell::sync::Lazy;
pub fn is_valid_ssn_programmatically(ssn: &str) -> bool {
let mut parts = ssn.split('-');
let (Some(area), Some(group), Some(serial), None) = (parts.next(), parts.next(), parts.next(), parts.next()) else {
return false;
};
if area.len() != 3 || group.len() != 2 || serial.len() != 4 {
return false;
}
let Some(area_num) = area.parse::<u16>().ok() else { return false; };
let Some(group_num) = group.parse::<u8>().ok() else { return false; };
let Some(serial_num) = serial.parse::<u16>().ok() else { return false; };
let invalid_area = (area_num == 0) || (area_num == 666) || (area_num >= 800);
let invalid_group = group_num == 0;
let invalid_serial = serial_num == 0;
!(invalid_area || invalid_group || invalid_serial)
}
static INVALID_NINO_PREFIXES: Lazy<HashSet<&'static str>> = Lazy::new(|| {
let mut set = HashSet::new();
set.extend(["BF", "BG", "EH", "GB", "JE", "NK", "KN", "LI", "NT", "TN", "ZZ"]);
set
});
static INVALID_NINO_PREFIX_CHARS: Lazy<HashSet<char>> = Lazy::new(|| {
let mut set = HashSet::new();
set.extend(['D', 'F', 'I', 'Q', 'U', 'V', 'O']);
set
});
static VALID_NINO_SUFFIX_CHARS: Lazy<HashSet<char>> = Lazy::new(|| {
let mut set = HashSet::new();
set.extend(['A', 'B', 'C', 'D']);
set
});
pub fn is_valid_uk_nino_programmatically(nino: &str) -> bool {
const NINO_LENGTH: usize = 9;
let nino_normalized: Cow<str> = if nino.chars().any(|c: char| c.is_ascii_lowercase()) {
Cow::Owned(nino.to_uppercase())
} else {
Cow::Borrowed(nino)
};
let nino_no_spaces = nino_normalized.chars().filter(|c| !c.is_whitespace()).collect::<String>();
if nino_no_spaces.len() != NINO_LENGTH {
return false;
}
let mut chars = nino_no_spaces.chars();
let (Some(prefix_char1), Some(prefix_char2)) = (chars.next(), chars.next()) else { return false; };
if !prefix_char1.is_ascii_alphabetic() || !prefix_char2.is_ascii_alphabetic() {
return false;
}
let prefix_str = &nino_no_spaces[0..2];
if INVALID_NINO_PREFIXES.contains(prefix_str) {
return false;
}
if INVALID_NINO_PREFIX_CHARS.contains(&prefix_char1) || INVALID_NINO_PREFIX_CHARS.contains(&prefix_char2) {
return false;
}
if !chars.by_ref().take(6).all(|c| c.is_ascii_digit()) {
return false;
}
let Some(suffix_char) = chars.next() else { return false; };
if !VALID_NINO_SUFFIX_CHARS.contains(&suffix_char) {
return false;
}
if chars.next().is_some() {
return false;
}
true
}
pub fn is_valid_luhn(num_str: &str) -> bool {
let mut sum = 0;
let mut alternate = false;
for c in num_str.chars().rev() {
let Some(mut digit) = c.to_digit(10) else { return false; };
if alternate {
digit *= 2;
if digit > 9 {
digit -= 9;
}
}
sum += digit;
alternate = !alternate;
}
sum % 10 == 0
}
pub fn is_valid_credit_card_programmatically(cc_number: &str) -> bool {
let digits: String = cc_number.chars().filter(|c| c.is_ascii_digit()).collect();
if digits.is_empty() {
return false;
}
is_valid_luhn(&digits)
}