luhn2/
lib.rs

1extern crate num;
2extern crate num_digitize;
3
4use num::*;
5use num_digitize::ToDigits;
6
7
8/// Takes a borrow of vector containing digits of credit card `&Vec<u8>` and a function
9/// `take_function(&usize) -> bool`. This function is called for each item of digit vector and if
10/// `take_function(digit) == true`, then it is inserted onto index 0 of resulting vector.
11/// By this action, the resulting vector is reversed.
12fn take_digits<F>(card_number_digits: &Vec<i8>, mut take_function: F) -> Vec<i8>
13    where F: FnMut(&usize) -> bool
14{
15    let mut result: Vec<i8> = Vec::new();
16    for item in card_number_digits.iter().enumerate() {
17        if take_function(&item.0) {
18            result.insert(0, *item.1);
19        }
20    }
21    result
22}
23
24/// Since we can't properly use `.sum()` on `Iterator` because of the
25/// 'iter_arith': bounds recently changed (see issue #27739), we will sum manually.
26fn sum(vector: &Vec<i8>) -> u64 {
27    let mut result: u64 = 0;
28    for item in vector {
29        result += *item as u64;
30    }
31    result
32}
33
34
35/// Luhn algorithm for credit card number validation (may be used for other purposes).
36///
37/// # Arguments
38///
39/// * `number` - number to validate.
40///
41/// # Example
42///
43/// ```
44/// use luhn2::validate;
45///
46/// let number: u64 = 49927398716;
47/// assert!(validate(number));
48/// ```
49pub fn validate(number: u64) -> bool {
50    let vec = {
51        let mut vec = number.to_digits();
52        vec.reverse();
53        vec
54    };
55
56    // Sum odd indexed digits. We pass `is_even()` function because we index from 0, not from 1.
57    let odd_sum: u64 = sum(&take_digits(&vec, Integer::is_even));
58    let even_sum: u64 = {
59        let mut even_sum: u64 = 0;
60        // Take even indexed digits (indexed from 0).
61        let even_digits = take_digits(&vec, Integer::is_odd);
62        // Multiply each even indexed number by 2.
63        for even_digit in even_digits.iter().map(|x| x * 2) {
64            // Convert each number to digits and sum them.
65            even_sum += sum(&even_digit.to_digits());
66        }
67        even_sum
68    };
69
70    let luhn = odd_sum + even_sum;
71    return luhn % 10 == 0;
72}
73
74
75#[test]
76fn test_sum() {
77    assert!(sum(&vec![1, 2, 3, 4]) == 10);
78    assert!(sum(&vec![]) == 0);
79}
80
81#[test]
82fn test_take_digits() {
83    let test_digits = vec![0, 1, 2, 3, 4, 5, 6];
84    assert!(take_digits(&test_digits, Integer::is_even) == vec![6, 4, 2, 0]);
85    assert!(take_digits(&test_digits, Integer::is_odd) == vec![5, 3, 1]);
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn test_ok_numbers() {
94        assert!(validate(49927398716));
95        assert!(validate(1234567812345670));
96        assert!(validate(79927398713));
97    }
98
99    #[test]
100    fn test_invalid_numbers() {
101        assert!(validate(4242424242424241) == false);
102        assert!(validate(49927398717) == false);
103        assert!(validate(1234567812345678) == false);
104    }
105}