use super::extract_digits;
#[must_use]
pub fn validate(card: &str) -> bool {
let digits = extract_digits(card);
if digits.len() != 16 {
return false;
}
luhn_ok(&digits)
}
#[must_use]
pub fn bank(card: &str) -> Option<&'static str> {
bank_entry(card).map(|(_, en, _)| *en)
}
#[must_use]
pub fn bank_persian(card: &str) -> Option<&'static str> {
bank_entry(card).map(|(_, _, fa)| *fa)
}
fn luhn_ok(digits: &str) -> bool {
let bytes = digits.as_bytes();
let mut sum: u32 = 0;
for (i, b) in bytes.iter().rev().enumerate() {
let d = (*b - b'0') as u32;
if i % 2 == 1 {
let doubled = d * 2;
sum += if doubled > 9 { doubled - 9 } else { doubled };
} else {
sum += d;
}
}
sum.is_multiple_of(10)
}
fn bank_entry(card: &str) -> Option<&'static (&'static str, &'static str, &'static str)> {
let digits = extract_digits(card);
if digits.len() != 16 || !luhn_ok(&digits) {
return None;
}
let bin = &digits[..6];
super::banks::CARD_BINS.iter().find(|(b, _, _)| *b == bin)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn known_valid_card() {
assert!(validate("6037990000000006"));
}
#[test]
fn rejects_bad_luhn() {
assert!(!validate("6037997599999990"));
}
#[test]
fn rejects_wrong_length() {
assert!(!validate("12345"));
assert!(!validate("60379900000000060"));
}
#[test]
fn accepts_separators() {
assert!(validate("6037-9900-0000-0006"));
assert!(validate("6037 9900 0000 0006"));
}
#[test]
fn accepts_persian_digits() {
assert!(validate("۶۰۳۷۹۹۰۰۰۰۰۰۰۰۰۶"));
}
#[test]
fn bank_lookup_returns_melli() {
assert_eq!(bank("6037990000000006"), Some("Bank Melli Iran"));
assert_eq!(bank_persian("6037990000000006"), Some("بانک ملی ایران"));
}
#[test]
fn bank_lookup_invalid_card() {
assert_eq!(bank("6037997599999990"), None);
}
}