use super::registry::{NamedPredicate, PredicateRegistry};
use serde_json::Value;
use std::sync::Arc;
pub fn register_financial_predicates(registry: &mut PredicateRegistry) {
registry.register(Arc::new(IsItinPredicate));
registry.register(Arc::new(IsAtinPredicate));
registry.register(Arc::new(IsCusipPredicate));
registry.register(Arc::new(IsAbaRoutingPredicate));
registry.register(Arc::new(IsMccPredicate));
registry.register(Arc::new(IsAccountNumberPredicate));
registry.register(Arc::new(IsCreditCardPredicate));
registry.register(Arc::new(IsCvvPredicate));
registry.register(Arc::new(IsCardExpiryPredicate));
registry.register(Arc::new(IsIbanPredicate));
registry.register(Arc::new(IsBicPredicate));
registry.register(Arc::new(IsSwiftPredicate));
registry.register(Arc::new(IsVinPredicate));
registry.register(Arc::new(IsUpcPredicate));
registry.register(Arc::new(IsEanPredicate));
registry.register(Arc::new(IsIsbnPredicate));
}
struct IsItinPredicate;
impl NamedPredicate for IsItinPredicate {
fn name(&self) -> &str {
"is_itin"
}
fn evaluate(&self, value: &Value, _args: &Value) -> bool {
let s = match value.as_str() {
Some(s) => s,
None => return false,
};
let digits: String = s.chars().filter(|c| c.is_ascii_digit()).collect();
digits.len() == 9 && is_valid_itin(&digits)
}
}
fn is_valid_itin(digits: &str) -> bool {
if digits.len() != 9 {
return false;
}
let first = digits.chars().next().unwrap();
let fourth = digits.chars().nth(3).unwrap();
first == '9' && (fourth == '7' || fourth == '8')
}
struct IsAtinPredicate;
impl NamedPredicate for IsAtinPredicate {
fn name(&self) -> &str {
"is_atin"
}
fn evaluate(&self, value: &Value, _args: &Value) -> bool {
let s = match value.as_str() {
Some(s) => s,
None => return false,
};
let digits: String = s.chars().filter(|c| c.is_ascii_digit()).collect();
digits.len() == 9 && is_valid_atin(&digits)
}
}
fn is_valid_atin(digits: &str) -> bool {
if digits.len() != 9 {
return false;
}
let first = digits.chars().next().unwrap();
let fourth_fifth = &digits[3..5];
first == '9' && fourth_fifth == "93"
}
struct IsCusipPredicate;
impl NamedPredicate for IsCusipPredicate {
fn name(&self) -> &str {
"is_cusip"
}
fn evaluate(&self, value: &Value, args: &Value) -> bool {
let s = match value.as_str() {
Some(s) => s.trim().to_uppercase(),
None => return false,
};
if s.len() != 9 {
return false;
}
if !s.chars().all(|c| c.is_ascii_alphanumeric()) {
return false;
}
let validate_checksum = args
.get("validate_checksum")
.and_then(|v| v.as_bool())
.unwrap_or(true);
if validate_checksum {
validate_cusip_checksum(&s)
} else {
true
}
}
}
fn validate_cusip_checksum(cusip: &str) -> bool {
let chars: Vec<char> = cusip.chars().collect();
if chars.len() != 9 {
return false;
}
let mut sum = 0;
for (i, c) in chars[..8].iter().enumerate() {
let val = if c.is_ascii_digit() {
c.to_digit(10).unwrap() as usize
} else if c.is_ascii_uppercase() {
(*c as usize) - ('A' as usize) + 10
} else {
return false;
};
let val = if i % 2 == 1 { val * 2 } else { val };
sum += val / 10 + val % 10;
}
let check_digit = (10 - (sum % 10)) % 10;
let expected = chars[8].to_digit(10);
expected == Some(check_digit as u32)
}
struct IsAbaRoutingPredicate;
impl NamedPredicate for IsAbaRoutingPredicate {
fn name(&self) -> &str {
"is_aba_routing"
}
fn evaluate(&self, value: &Value, _args: &Value) -> bool {
let s = match value.as_str() {
Some(s) => s.trim(),
None => return false,
};
let digits: String = s.chars().filter(|c| c.is_ascii_digit()).collect();
if digits.len() != 9 {
return false;
}
let prefix: u32 = digits[0..2].parse().unwrap_or(0);
let valid_prefix = matches!(prefix, 0..=12 | 21..=32 | 61..=72 | 80);
if !valid_prefix {
return false;
}
let d: Vec<u32> = digits.chars().map(|c| c.to_digit(10).unwrap()).collect();
let checksum = 3 * (d[0] + d[3] + d[6]) + 7 * (d[1] + d[4] + d[7]) + (d[2] + d[5] + d[8]);
checksum.is_multiple_of(10)
}
}
struct IsMccPredicate;
impl NamedPredicate for IsMccPredicate {
fn name(&self) -> &str {
"is_mcc"
}
fn evaluate(&self, value: &Value, _args: &Value) -> bool {
let s = match value.as_str() {
Some(s) => s.trim(),
None => return false,
};
s.len() == 4 && s.chars().all(|c| c.is_ascii_digit())
}
}
struct IsAccountNumberPredicate;
impl NamedPredicate for IsAccountNumberPredicate {
fn name(&self) -> &str {
"is_account_number"
}
fn evaluate(&self, value: &Value, args: &Value) -> bool {
let s = match value.as_str() {
Some(s) => s.trim(),
None => return false,
};
let min_len = args.get("min_len").and_then(|v| v.as_u64()).unwrap_or(1) as usize;
let max_len = args.get("max_len").and_then(|v| v.as_u64()).unwrap_or(30) as usize;
if s.len() < min_len || s.len() > max_len {
return false;
}
let allow_hyphens = args
.get("allow_hyphens")
.and_then(|v| v.as_bool())
.unwrap_or(true);
s.chars()
.all(|c| c.is_ascii_alphanumeric() || (allow_hyphens && c == '-'))
}
}
struct IsCreditCardPredicate;
impl NamedPredicate for IsCreditCardPredicate {
fn name(&self) -> &str {
"is_credit_card"
}
fn evaluate(&self, value: &Value, args: &Value) -> bool {
let s = match value.as_str() {
Some(s) => s,
None => return false,
};
let digits: String = s.chars().filter(|c| c.is_ascii_digit()).collect();
if !super::tin::luhn_check(&digits) {
return false;
}
let network = args.get("network").and_then(|v| v.as_str());
match network {
Some("visa") => is_visa(&digits),
Some("mastercard") => is_mastercard(&digits),
Some("amex") => is_amex(&digits),
Some("discover") => is_discover(&digits),
_ => {
is_visa(&digits)
|| is_mastercard(&digits)
|| is_amex(&digits)
|| is_discover(&digits)
}
}
}
}
fn is_visa(digits: &str) -> bool {
digits.len() == 16 && digits.starts_with('4')
}
fn is_mastercard(digits: &str) -> bool {
if digits.len() != 16 {
return false;
}
if let Ok(prefix2) = digits[0..2].parse::<u32>() {
if (51..=55).contains(&prefix2) {
return true;
}
}
if let Ok(prefix4) = digits[0..4].parse::<u32>() {
if (2221..=2720).contains(&prefix4) {
return true;
}
}
false
}
fn is_amex(digits: &str) -> bool {
digits.len() == 15 && (digits.starts_with("34") || digits.starts_with("37"))
}
fn is_discover(digits: &str) -> bool {
if digits.len() != 16 {
return false;
}
if digits.starts_with("6011") || digits.starts_with("65") {
return true;
}
if let Ok(prefix3) = digits[0..3].parse::<u32>() {
if (644..=649).contains(&prefix3) {
return true;
}
}
false
}
struct IsCvvPredicate;
impl NamedPredicate for IsCvvPredicate {
fn name(&self) -> &str {
"is_cvv"
}
fn evaluate(&self, value: &Value, args: &Value) -> bool {
let s = match value.as_str() {
Some(s) => s.trim(),
None => return false,
};
if !s.chars().all(|c| c.is_ascii_digit()) || s.is_empty() {
return false;
}
let network = args.get("network").and_then(|v| v.as_str());
match network {
Some("amex") => s.len() == 4,
Some(_) => s.len() == 3,
None => s.len() == 3 || s.len() == 4,
}
}
}
struct IsCardExpiryPredicate;
impl NamedPredicate for IsCardExpiryPredicate {
fn name(&self) -> &str {
"is_card_expiry"
}
fn evaluate(&self, value: &Value, args: &Value) -> bool {
let s = match value.as_str() {
Some(s) => s.trim(),
None => return false,
};
let parts: Vec<&str> = s.split('/').collect();
if parts.len() != 2 {
return false;
}
let month_str = parts[0];
let year_str = parts[1];
if month_str.len() != 2 || !month_str.chars().all(|c| c.is_ascii_digit()) {
return false;
}
if (year_str.len() != 2 && year_str.len() != 4)
|| !year_str.chars().all(|c| c.is_ascii_digit())
{
return false;
}
let month: u32 = match month_str.parse() {
Ok(m) => m,
Err(_) => return false,
};
if !(1..=12).contains(&month) {
return false;
}
let reject_expired = args
.get("reject_expired")
.and_then(|v| v.as_bool())
.unwrap_or(false);
if reject_expired {
let year: u32 = match year_str.parse() {
Ok(y) => y,
Err(_) => return false,
};
let full_year = if year < 100 { 2000 + year } else { year };
let (cur_year, cur_month) = current_year_month();
if full_year < cur_year || (full_year == cur_year && month < cur_month) {
return false;
}
}
true
}
}
fn current_year_month() -> (u32, u32) {
use std::time::{SystemTime, UNIX_EPOCH};
let secs = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
let days = (secs / 86400) as i64;
let z = days + 719468;
let era = if z >= 0 { z } else { z - 146096 } / 146097;
let doe = (z - era * 146097) as u32;
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
let y = (yoe as i64) + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let m = if mp < 10 { mp + 3 } else { mp - 9 };
let year = if m <= 2 { y + 1 } else { y };
(year as u32, m)
}
struct IsIbanPredicate;
impl NamedPredicate for IsIbanPredicate {
fn name(&self) -> &str {
"is_iban"
}
fn evaluate(&self, value: &Value, args: &Value) -> bool {
let s = match value.as_str() {
Some(s) => s,
None => return false,
};
let mut iban = Vec::with_capacity(s.len().min(34));
for byte in s.bytes() {
if byte.is_ascii_whitespace() {
continue;
}
let upper = byte.to_ascii_uppercase();
if !upper.is_ascii_alphanumeric() {
return false;
}
iban.push(upper);
if iban.len() > 34 {
return false;
}
}
if iban.len() < 5 {
return false;
}
if !iban[0].is_ascii_uppercase() || !iban[1].is_ascii_uppercase() {
return false;
}
let country = std::str::from_utf8(&iban[0..2]).unwrap();
if !iban[2].is_ascii_digit() || !iban[3].is_ascii_digit() {
return false;
}
if let Some(required_country) = args.get("country").and_then(|v| v.as_str()) {
if !country.eq_ignore_ascii_case(required_country) {
return false;
}
}
if let Some(expected_len) = iban_country_length(country) {
if iban.len() != expected_len {
return false;
}
}
iban_mod97(&iban)
}
}
fn iban_mod97(iban: &[u8]) -> bool {
let mut remainder: u32 = 0;
for &byte in &iban[4..] {
remainder = iban_mod97_step(byte, remainder);
}
for &byte in &iban[..4] {
remainder = iban_mod97_step(byte, remainder);
}
remainder == 1
}
fn iban_mod97_step(byte: u8, remainder: u32) -> u32 {
if byte.is_ascii_digit() {
return (remainder * 10 + u32::from(byte - b'0')) % 97;
}
(remainder * 100 + u32::from(byte - b'A' + 10)) % 97
}
fn iban_country_length(country: &str) -> Option<usize> {
match country {
"AL" => Some(28),
"AD" => Some(24),
"AT" => Some(20),
"AZ" => Some(28),
"BH" => Some(22),
"BY" => Some(28),
"BE" => Some(16),
"BA" => Some(20),
"BR" => Some(29),
"BG" => Some(22),
"CR" => Some(22),
"HR" => Some(21),
"CY" => Some(28),
"CZ" => Some(24),
"DK" => Some(18),
"DO" => Some(28),
"EE" => Some(20),
"FO" => Some(18),
"FI" => Some(18),
"FR" => Some(27),
"GE" => Some(22),
"DE" => Some(22),
"GI" => Some(23),
"GR" => Some(27),
"GL" => Some(18),
"GT" => Some(28),
"HU" => Some(28),
"IS" => Some(26),
"IQ" => Some(23),
"IE" => Some(22),
"IL" => Some(23),
"IT" => Some(27),
"JO" => Some(30),
"KZ" => Some(20),
"XK" => Some(20),
"KW" => Some(30),
"LV" => Some(21),
"LB" => Some(28),
"LI" => Some(21),
"LT" => Some(20),
"LU" => Some(20),
"MK" => Some(19),
"MT" => Some(31),
"MR" => Some(27),
"MU" => Some(30),
"MC" => Some(27),
"MD" => Some(24),
"ME" => Some(22),
"NL" => Some(18),
"NO" => Some(15),
"PK" => Some(24),
"PS" => Some(29),
"PL" => Some(28),
"PT" => Some(25),
"QA" => Some(29),
"RO" => Some(24),
"LC" => Some(32),
"SM" => Some(27),
"SA" => Some(24),
"RS" => Some(22),
"SC" => Some(31),
"SK" => Some(24),
"SI" => Some(19),
"ES" => Some(24),
"SE" => Some(24),
"CH" => Some(21),
"TN" => Some(24),
"TR" => Some(26),
"AE" => Some(23),
"GB" => Some(22),
"VA" => Some(22),
"VG" => Some(24),
"UA" => Some(29),
_ => None,
}
}
struct IsBicPredicate;
impl NamedPredicate for IsBicPredicate {
fn name(&self) -> &str {
"is_bic"
}
fn evaluate(&self, value: &Value, _args: &Value) -> bool {
let s = match value.as_str() {
Some(s) => s.trim().to_uppercase(),
None => return false,
};
if s.len() != 8 && s.len() != 11 {
return false;
}
if !s[0..4].chars().all(|c| c.is_ascii_uppercase()) {
return false;
}
if !s[4..6].chars().all(|c| c.is_ascii_uppercase()) {
return false;
}
if !s[6..8].chars().all(|c| c.is_ascii_alphanumeric()) {
return false;
}
if s.len() == 11 && !s[8..11].chars().all(|c| c.is_ascii_alphanumeric()) {
return false;
}
true
}
}
struct IsSwiftPredicate;
impl NamedPredicate for IsSwiftPredicate {
fn name(&self) -> &str {
"is_swift"
}
fn evaluate(&self, value: &Value, args: &Value) -> bool {
IsBicPredicate.evaluate(value, args)
}
}
struct IsVinPredicate;
impl NamedPredicate for IsVinPredicate {
fn name(&self) -> &str {
"is_vin"
}
fn evaluate(&self, value: &Value, args: &Value) -> bool {
let s = match value.as_str() {
Some(s) => s.trim().to_uppercase(),
None => return false,
};
if s.len() != 17 {
return false;
}
if !s
.chars()
.all(|c| (c.is_ascii_alphanumeric()) && c != 'I' && c != 'O' && c != 'Q')
{
return false;
}
let validate_checksum = args
.get("validate_checksum")
.and_then(|v| v.as_bool())
.unwrap_or(true);
if !validate_checksum {
return true;
}
vin_check_digit(&s)
}
}
fn vin_transliterate(c: char) -> Option<u32> {
match c {
'0'..='9' => Some(c as u32 - '0' as u32),
'A' | 'J' => Some(1),
'B' | 'K' | 'S' => Some(2),
'C' | 'L' | 'T' => Some(3),
'D' | 'M' | 'U' => Some(4),
'E' | 'N' | 'V' => Some(5),
'F' | 'W' => Some(6),
'G' | 'P' | 'X' => Some(7),
'H' | 'Y' => Some(8),
'R' | 'Z' => Some(9),
_ => None, }
}
const VIN_WEIGHTS: [u32; 17] = [8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2];
fn vin_check_digit(vin: &str) -> bool {
let mut sum: u32 = 0;
for (i, c) in vin.chars().enumerate() {
if i == 8 {
continue; }
let val = match vin_transliterate(c) {
Some(v) => v,
None => return false,
};
sum += val * VIN_WEIGHTS[i];
}
let remainder = sum % 11;
let expected = if remainder == 10 {
'X'
} else {
char::from(b'0' + remainder as u8)
};
vin.chars().nth(8) == Some(expected)
}
struct IsUpcPredicate;
impl NamedPredicate for IsUpcPredicate {
fn name(&self) -> &str {
"is_upc"
}
fn evaluate(&self, value: &Value, _args: &Value) -> bool {
let s = match value.as_str() {
Some(s) => s.trim(),
None => return false,
};
let digits: Vec<u32> = s.chars().filter_map(|c| c.to_digit(10)).collect();
if digits.len() != 12 {
return false;
}
let cleaned: String = s
.chars()
.filter(|c| !c.is_whitespace() && *c != '-')
.collect();
if cleaned.len() != 12 || !cleaned.chars().all(|c| c.is_ascii_digit()) {
return false;
}
let mut sum: u32 = 0;
for (i, &d) in digits.iter().enumerate() {
if i % 2 == 0 {
sum += d * 3;
} else {
sum += d;
}
}
sum.is_multiple_of(10)
}
}
struct IsEanPredicate;
impl NamedPredicate for IsEanPredicate {
fn name(&self) -> &str {
"is_ean"
}
fn evaluate(&self, value: &Value, _args: &Value) -> bool {
let s = match value.as_str() {
Some(s) => s.trim(),
None => return false,
};
let digits: Vec<u32> = s.chars().filter_map(|c| c.to_digit(10)).collect();
if digits.len() != 13 {
return false;
}
let cleaned: String = s
.chars()
.filter(|c| !c.is_whitespace() && *c != '-')
.collect();
if cleaned.len() != 13 || !cleaned.chars().all(|c| c.is_ascii_digit()) {
return false;
}
let mut sum: u32 = 0;
for (i, &d) in digits.iter().enumerate() {
if i % 2 == 0 {
sum += d;
} else {
sum += d * 3;
}
}
sum.is_multiple_of(10)
}
}
struct IsIsbnPredicate;
impl NamedPredicate for IsIsbnPredicate {
fn name(&self) -> &str {
"is_isbn"
}
fn evaluate(&self, value: &Value, args: &Value) -> bool {
let s = match value.as_str() {
Some(s) => s.trim(),
None => return false,
};
let version = args.get("version").and_then(|v| v.as_u64());
let cleaned: String = s
.chars()
.filter(|c| *c != '-' && !c.is_whitespace())
.collect();
match version {
Some(10) => isbn10_check(&cleaned),
Some(13) => isbn13_check(&cleaned),
_ => isbn10_check(&cleaned) || isbn13_check(&cleaned),
}
}
}
fn isbn10_check(s: &str) -> bool {
if s.len() != 10 {
return false;
}
let chars: Vec<char> = s.chars().collect();
if !chars[..9].iter().all(|c| c.is_ascii_digit()) {
return false;
}
let last = chars[9];
if !last.is_ascii_digit() && last != 'X' && last != 'x' {
return false;
}
let mut sum: u32 = 0;
for (i, &c) in chars.iter().enumerate() {
let val = if c == 'X' || c == 'x' {
10
} else {
c.to_digit(10).unwrap()
};
sum += val * (10 - i as u32);
}
sum.is_multiple_of(11)
}
fn isbn13_check(s: &str) -> bool {
if s.len() != 13 {
return false;
}
if !s.chars().all(|c| c.is_ascii_digit()) {
return false;
}
if !s.starts_with("978") && !s.starts_with("979") {
return false;
}
let digits: Vec<u32> = s.chars().filter_map(|c| c.to_digit(10)).collect();
let mut sum: u32 = 0;
for (i, &d) in digits.iter().enumerate() {
if i % 2 == 0 {
sum += d;
} else {
sum += d * 3;
}
}
sum.is_multiple_of(10)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_is_itin() {
let pred = IsItinPredicate;
assert!(pred.evaluate(&json!("912-78-1234"), &json!(null)));
assert!(pred.evaluate(&json!("900-70-0001"), &json!(null)));
assert!(pred.evaluate(&json!("999-80-9999"), &json!(null)));
assert!(!pred.evaluate(&json!("123-78-1234"), &json!(null)));
assert!(!pred.evaluate(&json!("912-68-1234"), &json!(null)));
assert!(!pred.evaluate(&json!("912-78-123"), &json!(null)));
}
#[test]
fn test_is_atin() {
let pred = IsAtinPredicate;
assert!(pred.evaluate(&json!("912-93-1234"), &json!(null)));
assert!(pred.evaluate(&json!("900-93-0001"), &json!(null)));
assert!(!pred.evaluate(&json!("123-93-1234"), &json!(null)));
assert!(!pred.evaluate(&json!("912-94-1234"), &json!(null)));
}
#[test]
fn test_is_cusip() {
let pred = IsCusipPredicate;
assert!(pred.evaluate(&json!("037833100"), &json!(null))); assert!(pred.evaluate(&json!("594918104"), &json!(null)));
assert!(pred.evaluate(&json!("037833100"), &json!(null)));
assert!(!pred.evaluate(&json!("03783310"), &json!(null)));
assert!(!pred.evaluate(&json!("0378331001"), &json!(null)));
assert!(!pred.evaluate(&json!("037833101"), &json!(null)));
assert!(pred.evaluate(&json!("037833101"), &json!({"validate_checksum": false})));
}
#[test]
fn test_is_aba_routing() {
let pred = IsAbaRoutingPredicate;
assert!(pred.evaluate(&json!("021000021"), &json!(null))); assert!(pred.evaluate(&json!("011401533"), &json!(null))); assert!(pred.evaluate(&json!("121000358"), &json!(null)));
assert!(pred.evaluate(&json!("021-000-021"), &json!(null)));
assert!(!pred.evaluate(&json!("02100002"), &json!(null)));
assert!(!pred.evaluate(&json!("021000022"), &json!(null)));
assert!(!pred.evaluate(&json!("991000021"), &json!(null)));
}
#[test]
fn test_is_mcc() {
let pred = IsMccPredicate;
assert!(pred.evaluate(&json!("5411"), &json!(null))); assert!(pred.evaluate(&json!("5812"), &json!(null))); assert!(pred.evaluate(&json!("0000"), &json!(null)));
assert!(pred.evaluate(&json!("9999"), &json!(null)));
assert!(!pred.evaluate(&json!("541"), &json!(null)));
assert!(!pred.evaluate(&json!("54111"), &json!(null)));
assert!(!pred.evaluate(&json!("541A"), &json!(null)));
}
#[test]
fn test_is_account_number() {
let pred = IsAccountNumberPredicate;
assert!(pred.evaluate(&json!("1234567890"), &json!(null)));
assert!(pred.evaluate(&json!("ABC-123-XYZ"), &json!(null)));
assert!(pred.evaluate(&json!("X"), &json!(null)));
assert!(!pred.evaluate(&json!("12"), &json!({"min_len": 5})));
assert!(!pred.evaluate(&json!("123456"), &json!({"max_len": 5})));
assert!(!pred.evaluate(&json!(""), &json!(null)));
assert!(!pred.evaluate(&json!("ABC-123"), &json!({"allow_hyphens": false})));
}
#[test]
fn test_is_credit_card() {
let pred = IsCreditCardPredicate;
assert!(pred.evaluate(&json!("4532015112830366"), &json!(null)));
assert!(pred.evaluate(&json!("4532015112830366"), &json!({"network": "visa"})));
assert!(pred.evaluate(&json!("371449635398431"), &json!(null)));
assert!(pred.evaluate(&json!("371449635398431"), &json!({"network": "amex"})));
assert!(pred.evaluate(&json!("5425233430109903"), &json!(null)));
assert!(pred.evaluate(
&json!("5425233430109903"),
&json!({"network": "mastercard"})
));
assert!(pred.evaluate(&json!("6011111111111117"), &json!(null)));
assert!(pred.evaluate(&json!("6011111111111117"), &json!({"network": "discover"})));
assert!(pred.evaluate(&json!("4532 0151 1283 0366"), &json!(null)));
assert!(!pred.evaluate(&json!("4532015112830366"), &json!({"network": "amex"})));
assert!(!pred.evaluate(&json!("371449635398431"), &json!({"network": "visa"})));
assert!(!pred.evaluate(&json!("4532015112830367"), &json!(null)));
assert!(!pred.evaluate(&json!("4532"), &json!(null)));
assert!(!pred.evaluate(&json!(4532015112830366_u64), &json!(null)));
}
#[test]
fn test_is_cvv() {
let pred = IsCvvPredicate;
assert!(pred.evaluate(&json!("123"), &json!(null)));
assert!(pred.evaluate(&json!("1234"), &json!(null)));
assert!(pred.evaluate(&json!("1234"), &json!({"network": "amex"})));
assert!(!pred.evaluate(&json!("123"), &json!({"network": "amex"})));
assert!(pred.evaluate(&json!("123"), &json!({"network": "visa"})));
assert!(!pred.evaluate(&json!("1234"), &json!({"network": "visa"})));
assert!(!pred.evaluate(&json!("12a"), &json!(null)));
assert!(!pred.evaluate(&json!("12"), &json!(null)));
assert!(!pred.evaluate(&json!("12345"), &json!(null)));
assert!(!pred.evaluate(&json!(123), &json!(null)));
}
#[test]
fn test_is_card_expiry() {
let pred = IsCardExpiryPredicate;
assert!(pred.evaluate(&json!("12/25"), &json!(null)));
assert!(pred.evaluate(&json!("01/30"), &json!(null)));
assert!(pred.evaluate(&json!("12/2025"), &json!(null)));
assert!(pred.evaluate(&json!("01/2030"), &json!(null)));
assert!(!pred.evaluate(&json!("13/25"), &json!(null)));
assert!(!pred.evaluate(&json!("00/25"), &json!(null)));
assert!(!pred.evaluate(&json!("12-25"), &json!(null)));
assert!(!pred.evaluate(&json!("12/2"), &json!(null)));
assert!(!pred.evaluate(&json!("1/25"), &json!(null)));
assert!(!pred.evaluate(&json!("01/20"), &json!({"reject_expired": true})));
assert!(!pred.evaluate(&json!("01/2020"), &json!({"reject_expired": true})));
assert!(pred.evaluate(&json!("12/99"), &json!({"reject_expired": true})));
assert!(pred.evaluate(&json!("12/2099"), &json!({"reject_expired": true})));
assert!(!pred.evaluate(&json!(1225), &json!(null)));
}
#[test]
fn test_is_iban() {
let pred = IsIbanPredicate;
assert!(pred.evaluate(&json!("GB29NWBK60161331926819"), &json!(null)));
assert!(pred.evaluate(&json!("DE89370400440532013000"), &json!(null)));
assert!(pred.evaluate(&json!("FR7630006000011234567890189"), &json!(null)));
assert!(pred.evaluate(&json!("ES9121000418450200051332"), &json!(null)));
assert!(pred.evaluate(&json!("IT60X0542811101000000123456"), &json!(null)));
assert!(pred.evaluate(&json!("GB29 NWBK 6016 1331 9268 19"), &json!(null)));
assert!(pred.evaluate(&json!("DE89 3704 0044 0532 0130 00"), &json!(null)));
assert!(pred.evaluate(&json!("DE89370400440532013000"), &json!({"country": "DE"})));
assert!(!pred.evaluate(&json!("DE89370400440532013000"), &json!({"country": "GB"})));
assert!(!pred.evaluate(&json!("GB29NWBK60161331926818"), &json!(null)));
assert!(!pred.evaluate(&json!("GB29NWBK6016133192681"), &json!(null)));
assert!(!pred.evaluate(&json!("GB29"), &json!(null)));
assert!(!pred.evaluate(&json!("GB29NWBK6016!331926819"), &json!(null)));
assert!(!pred.evaluate(&json!(123456), &json!(null)));
}
#[test]
fn test_is_bic() {
let pred = IsBicPredicate;
assert!(pred.evaluate(&json!("DEUTDEFF"), &json!(null))); assert!(pred.evaluate(&json!("BNPAFRPP"), &json!(null))); assert!(pred.evaluate(&json!("CHASUS33"), &json!(null)));
assert!(pred.evaluate(&json!("DEUTDEFF500"), &json!(null))); assert!(pred.evaluate(&json!("COBADEFFXXX"), &json!(null)));
assert!(pred.evaluate(&json!("deutdeff"), &json!(null)));
assert!(!pred.evaluate(&json!("DEUTDE"), &json!(null)));
assert!(!pred.evaluate(&json!("DEUTDEFF50"), &json!(null)));
assert!(!pred.evaluate(&json!("DEUTDEFF5000"), &json!(null)));
assert!(!pred.evaluate(&json!("D3UTDEFF"), &json!(null)));
assert!(!pred.evaluate(&json!("DEUT1EFF"), &json!(null)));
assert!(!pred.evaluate(&json!(12345678), &json!(null)));
}
#[test]
fn test_is_vin() {
let pred = IsVinPredicate;
assert!(pred.evaluate(&json!("11111111111111111"), &json!(null)));
assert!(pred.evaluate(&json!("1M8GDM9AXKP042788"), &json!(null)));
assert!(pred.evaluate(
&json!("12345678901234567"),
&json!({"validate_checksum": false})
));
assert!(!pred.evaluate(&json!("1234567890"), &json!(null)));
assert!(!pred.evaluate(&json!("1M8GDM9AXKPI42788"), &json!(null)));
assert!(!pred.evaluate(&json!("1M8GDM9AXKPO42788"), &json!(null)));
assert!(!pred.evaluate(&json!("1M8GDM9AXKPQ42788"), &json!(null)));
assert!(!pred.evaluate(&json!("1M8GDM9AXKP042789"), &json!(null)));
assert!(!pred.evaluate(&json!(12345), &json!(null)));
}
#[test]
fn test_is_upc() {
let pred = IsUpcPredicate;
assert!(pred.evaluate(&json!("036000291452"), &json!(null)));
assert!(pred.evaluate(&json!("012345678905"), &json!(null)));
assert!(!pred.evaluate(&json!("12345"), &json!(null)));
assert!(!pred.evaluate(&json!("036000291453"), &json!(null)));
assert!(!pred.evaluate(&json!(12345), &json!(null)));
}
#[test]
fn test_is_ean() {
let pred = IsEanPredicate;
assert!(pred.evaluate(&json!("4006381333931"), &json!(null)));
assert!(pred.evaluate(&json!("5901234123457"), &json!(null)));
assert!(!pred.evaluate(&json!("12345"), &json!(null)));
assert!(!pred.evaluate(&json!("4006381333932"), &json!(null)));
assert!(!pred.evaluate(&json!(12345), &json!(null)));
}
#[test]
fn test_is_isbn() {
let pred = IsIsbnPredicate;
assert!(pred.evaluate(&json!("0306406152"), &json!(null)));
assert!(pred.evaluate(&json!("0-306-40615-2"), &json!(null))); assert!(pred.evaluate(&json!("007462542X"), &json!(null)));
assert!(pred.evaluate(&json!("9780306406157"), &json!(null)));
assert!(pred.evaluate(&json!("978-0-306-40615-7"), &json!(null)));
assert!(pred.evaluate(&json!("0306406152"), &json!({"version": 10})));
assert!(!pred.evaluate(&json!("9780306406157"), &json!({"version": 10})));
assert!(pred.evaluate(&json!("9780306406157"), &json!({"version": 13})));
assert!(!pred.evaluate(&json!("0306406152"), &json!({"version": 13})));
assert!(!pred.evaluate(&json!("0306406153"), &json!(null)));
assert!(!pred.evaluate(&json!("9780306406158"), &json!(null)));
assert!(!pred.evaluate(&json!("12345"), &json!(null)));
assert!(!pred.evaluate(&json!(12345), &json!(null)));
}
}