1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//!
//! Provides 1 function to return mock password data.
//!
//! # Examples
//!
//! ```rust
//! use mockd::password;
//!     let upper = true;
//!     let numeric = true;
//!     let special = true;
//!     let num = 27;
//!
//!     let data = password::generate(upper, numeric, special, num); // #9e1Vv5s&Ng8L-#9@=!6+s1+0@R
//! ```
//!
//! # Feature
//!
//! Requires the "password" feature.
//!

use crate::misc;

/// Generate a random password.
///
/// # inputs
/// * upper - if true include upper case letters
/// * numeric - if true include numbers
/// * special - if true include symbols
/// * num - number of characters in password
///
/// The generator always uses lower case letters and enforces a minimum
/// password length of five characters.
///
/// # Example
///
/// ```rust
/// let password = mockd::password::generate(true, true, false, 10);
///
/// println!("Password: {}", password);
/// ```
///
/// # Feature
///
/// Requires the "password" feature.
///
pub fn generate(upper: bool, numeric: bool, special: bool, mut num: i8) -> String {
    if num < 5 {
        num = 5;
    }
    letter(upper, numeric, special, num)
}

fn letter(upper: bool, numeric: bool, special: bool, num: i8) -> String {
    let mut pw = String::from("");

    let mut opts = vec![1i8];
    if upper {
        opts.push(2);
    }
    if numeric {
        opts.push(3);
    }
    if special {
        opts.push(4);
    }

    let lower_str = b"abcdefghijklmnopqrstuvwxyz";
    let upper_str = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    let numeric_str = b"0123456789";
    let special_str = b"!@#$%&*+-=?";

    match opts[misc::random_data_index(opts.as_slice())] {
        1 => pw.push(misc::random_char_from_string(lower_str)),
        2 => pw.push(misc::random_char_from_string(upper_str)),
        3 => pw.push(misc::random_char_from_string(numeric_str)),
        4 => pw.push(misc::random_char_from_string(special_str)),
        _ => pw.push(misc::random_char_from_string(lower_str)),
    }

    match num {
        0 => pw,
        _ => format!("{}{}", pw, letter(upper, numeric, special, num - 1)),
    }
}

#[cfg(test)]
mod tests {
    use crate::password;
    use crate::testify::exec_mes;

    #[test]
    fn password_generate() {
        exec_mes("password::generate", || {
            password::generate(true, true, true, 26)
        });
    }
}