unreal/impls/
payment.rs

1use std::iter::repeat_with;
2
3use chrono::{Datelike, Local};
4use rand::seq::SliceRandom;
5use rand::{Rng, RngCore};
6
7use crate::Unreal;
8use crate::data::payment::CARD;
9
10/// Generate random payment data.
11impl<R: RngCore> Unreal<R> {
12    fn card(&mut self) -> (&'static str, &'static [&'static str]) {
13        *CARD.choose(self).expect("CARDS should not be empty")
14    }
15
16    /// Return a random credit card company from the credit card company data set.
17    pub fn card_type(&mut self) -> &str {
18        self.card().0
19    }
20
21    /// Return a random 16 digit credit card number. This number is not guaranteed to pass the Luhn
22    /// algorithm check. Use [`Self::credit_card_luhn_number`] to get a number which passes the
23    /// Luhn algorithm check.
24    pub fn credit_card_number(&mut self) -> String {
25        self.credit_card_number_inner().take(16).collect()
26    }
27
28    fn credit_card_number_inner(&mut self) -> impl Iterator<Item = char> {
29        let starts = Self::card(self).1;
30        let start =
31            *SliceRandom::choose(starts, self).expect("all cards should have starting digits");
32        start
33            .chars()
34            .chain(repeat_with(|| (b'0' + self.gen_range(0..=9)) as char))
35    }
36
37    /// Return a random 16 digit credit card number that passes the Luhn algorithm check.
38    pub fn credit_card_luhn_number(&mut self) -> String {
39        let number: String = self.credit_card_number_inner().take(15).collect();
40        let check_digit = ((10
41            - (number
42                .chars()
43                .enumerate()
44                .map(|(index, digit)| {
45                    let digit = digit as u8 - b'0';
46                    u32::from(if index % 2 == 0 {
47                        (digit * 2) % 9
48                    } else {
49                        digit
50                    })
51                })
52                .sum::<u32>()
53                % 10))
54            % 10)
55            .to_string();
56        number + &check_digit
57    }
58
59    /// Return a random credit card expiration string in the format `MM/YY`. The year is the current
60    /// year plus another random 1..=10 years.
61    pub fn credit_card_exp(&mut self) -> String {
62        let year = (Local::now().year() + self.gen_range(1..=10)).to_string();
63        let month = self.month();
64        format!("{:0>2}/{}", month, &year[year.len() - 2..])
65    }
66
67    /// Return three random digits for a credit card CVV.
68    pub fn credit_card_cvv(&mut self) -> String {
69        self.digits(3)
70    }
71}