dd_sds/secondary_validation/
luhn_checksum.rs1use crate::secondary_validation::{get_previous_digit, Validator};
2
3pub struct LuhnChecksum;
4
5impl Validator for LuhnChecksum {
6 fn is_valid_match(&self, regex_match: &str) -> bool {
7 let mut input_iter = regex_match.chars();
8
9 if let Some(checksum) = get_previous_digit(&mut input_iter) {
10 let mut sum: u32 = 0;
11 let mut is_odd = false;
12 while let Some(digit) = get_previous_digit(&mut input_iter) {
13 if is_odd {
14 sum += digit
15 } else if digit > 4 {
16 sum += digit * 2 - 9;
17 } else {
18 sum += digit * 2
19 }
20 is_odd = !is_odd;
21 }
22 return (10 - (sum % 10)) % 10 == checksum;
23 }
24 false
25 }
26}
27
28#[cfg(test)]
29mod test {
30 use crate::secondary_validation::*;
31
32 #[test]
33 fn validate_various_credit_cards() {
34 let credit_cards = vec![
35 "3782 822463 10005",
38 "3714 4963 5398 431",
39 "378734493671000",
41 "5610591081018250",
43 "3056 930902 5904",
45 "3852 0000 0232 37",
46 "6011111111111117",
48 "6011 0009 9013 9424",
49 "3530111333300000",
51 "35660020 20360505",
52 "5555555555554444",
54 "5105 1051 0510 5100",
55 "4111 1111 1111 1111",
57 "40128888 88881881",
58 "4222222222222",
59 "5019717010103742",
61 "6331101999990016",
63 ];
64 for credit_card in credit_cards {
65 println!("credit card input: {credit_card}");
66 assert!(LuhnChecksum.is_valid_match(credit_card));
67
68 let (split_credit_card, last_digit) = credit_card.split_at(credit_card.len() - 1);
69 let mut wrong_credit_card = split_credit_card.to_string();
70 wrong_credit_card
71 .push_str(&((last_digit.parse::<u32>().unwrap() + 1) * 2 % 10).to_string());
72
73 println!("wrong credit card input: {wrong_credit_card}");
74
75 assert!(!LuhnChecksum.is_valid_match(&wrong_credit_card));
76 }
77 }
78
79 #[test]
80 fn skip_non_digit_characters() {
81 assert!(LuhnChecksum.is_valid_match("378282246310005"));
82 assert!(LuhnChecksum.is_valid_match("3 7 8 2 8 2 2 4ABC, 6 3 1 🎅0 0 0 5"));
84 }
85}