simple_password_generator/
lib.rs

1//! # Simple Password Generator
2//!
3//! A library for generating passwords
4
5use rand::Rng;
6use std::char;
7
8static DEFAULT_LENGTH: u8 = 8;
9static ALPHABET: [char; 26] = [
10    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
11    't', 'u', 'v', 'w', 'x', 'y', 'z',
12];
13static NUMBERS: [char; 10] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
14static SPECIAL_CHARS: [char; 11] = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '~'];
15
16/// PasswordGenerator struct for generatoring passwords
17pub struct PasswordGenerator {
18    /// Array of lowercase chars used when generating a password.
19    lowercase_char_set: [char; 26],
20    /// Array of uppercase chars used when generating a password.
21    uppercase_char_set: [char; 26],
22    /// Array of number chars used when generating a password.
23    number_set: [char; 10],
24    /// Array of special chars used when generating a password.
25    spec_char_set: [char; 11],
26    /// Array of composition codes used when generating a password.
27    composition_codes: [CompositionCodes; 4],
28    /// Length of password to be generated.
29    ///
30    /// Default `8`
31    length: u8,
32    /// Bool used to exclude uppercase composition code when generating a password.
33    ///
34    /// Default `false`
35    lowercase_only: bool,
36    /// Bool used to exclude lowercase composition code when generating a password.
37    ///
38    /// Default `false`
39    uppercase_only: bool,
40    /// Bool used to exclude number composition code when generating a password.
41    ///
42    /// Default `false`
43    exclude_numbers: bool,
44    /// Bool used to special character composition code when generating a password.
45    ///
46    /// Default `false`
47    exclude_special_chars: bool,
48}
49
50/// The primary character types used when generating the composition of the password
51pub enum CompositionCodes {
52    Lowercase,
53    Uppercase,
54    Number,
55    SpecialCharacter,
56}
57
58impl CompositionCodes {
59    /// Outputs all compositions code in a array
60    pub fn all_to_array() -> [CompositionCodes; 4] {
61        [
62            CompositionCodes::Lowercase,
63            CompositionCodes::Uppercase,
64            CompositionCodes::Number,
65            CompositionCodes::SpecialCharacter,
66        ]
67    }
68}
69
70impl PasswordGenerator {
71    /// PasswordGenerator Constructor
72    pub fn new() -> PasswordGenerator {
73        let lowercase_char_set = ALPHABET;
74        let uppercase_char_set = lowercase_char_set.map(|lower_char| {
75            let uppered: Vec<char> = lower_char.to_uppercase().collect();
76            uppered[0]
77        });
78
79        PasswordGenerator {
80            lowercase_char_set,
81            uppercase_char_set,
82            number_set: NUMBERS,
83            spec_char_set: SPECIAL_CHARS,
84            composition_codes: CompositionCodes::all_to_array(),
85            length: DEFAULT_LENGTH,
86            lowercase_only: false,
87            uppercase_only: false,
88            exclude_numbers: false,
89            exclude_special_chars: false,
90        }
91    }
92
93    /// Builder function for setting password length
94    ///
95    /// # Examples
96    ///
97    /// ```
98    /// use simple_password_generator::PasswordGenerator;
99    ///
100    /// let expected_length = 16;
101    /// let result = PasswordGenerator::new().length(expected_length).generate();
102    ///
103    /// assert_eq!(expected_length as usize, result.chars().count());
104    /// ```
105    pub fn length(mut self, length: u8) -> Self {
106        self.length = length;
107
108        self
109    }
110
111    /// Builder function for setting lowercase characters only
112    ///
113    /// # Examples
114    ///
115    /// ```
116    /// use simple_password_generator::PasswordGenerator;
117    ///
118    /// let test_password = PasswordGenerator::new().lowercase_only(true).generate();
119    /// let mut contains_uppercase = false;
120    ///
121    /// for c in test_password.chars() {
122    /// 	if c.is_alphabetic() {
123    /// 		if c.is_uppercase() {
124    /// 			contains_uppercase = true
125    /// 		}
126    /// 	}
127    /// }
128    ///
129    /// assert_eq!(false, contains_uppercase);
130    /// ```
131    pub fn lowercase_only(mut self, lowercase_only: bool) -> Self {
132        self.lowercase_only = lowercase_only;
133
134        self
135    }
136
137    /// Builder function for setting uppercase characters only
138    ///
139    /// # Examples
140    ///
141    /// ```
142    /// use simple_password_generator::PasswordGenerator;
143    ///
144    /// let test_password = PasswordGenerator::new().uppercase_only(true).generate();
145    /// let mut contains_lowercase = false;
146    ///
147    /// for c in test_password.chars() {
148    /// 	if c.is_alphabetic() {
149    /// 		if c.is_lowercase() {
150    /// 			contains_lowercase = true
151    /// 		}
152    /// 	}
153    /// }
154    ///
155    /// assert_eq!(false, contains_lowercase);
156    /// ```
157    pub fn uppercase_only(mut self, uppercase_only: bool) -> Self {
158        self.uppercase_only = uppercase_only;
159
160        self
161    }
162
163    /// Builder function for excluding any numbers from password
164    ///
165    /// # Examples
166    ///
167    /// ```
168    /// use simple_password_generator::PasswordGenerator;
169    ///
170    /// let test_password = PasswordGenerator::new().exclude_numbers(true).generate();
171    /// let mut contains_numbers = false;
172    ///
173    /// for c in test_password.chars() {
174    /// 	if c.is_numeric() {
175    /// 		contains_numbers = true
176    /// 	}
177    /// }
178    ///
179    /// assert_eq!(false, contains_numbers);
180    /// ```
181    pub fn exclude_numbers(mut self, exclude_numbers: bool) -> Self {
182        self.exclude_numbers = exclude_numbers;
183
184        self
185    }
186
187    /// Builder function for excluding any special characters from password
188    ///
189    /// # Examples
190    ///
191    /// ```
192    /// use simple_password_generator::PasswordGenerator;
193    ///
194    /// let test_password = PasswordGenerator::new()
195    /// 	.exclude_special_chars(true)
196    /// 	.generate();
197    /// let spec_char_vec: Vec<char> = vec!['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '~'];
198    /// let mut contains_special_chars = false;
199    ///
200    /// for c in test_password.chars() {
201    /// 	if spec_char_vec.contains(&c) {
202    /// 		contains_special_chars = true;
203    /// 	}
204    /// }
205    ///
206    /// assert_eq!(false, contains_special_chars);
207    /// ```
208    pub fn exclude_special_chars(mut self, exclude_special_chars: bool) -> Self {
209        self.exclude_special_chars = exclude_special_chars;
210
211        self
212    }
213
214    /// Returns a random lowercase char
215    fn get_random_lowercase_char(&self) -> char {
216        let mut rng = rand::thread_rng();
217        let rnd_index = rng.gen_range(0..=self.lowercase_char_set.len() - 1);
218
219        self.lowercase_char_set[rnd_index]
220    }
221
222    /// Returns a random uppercase char
223    fn get_random_uppercase_char(&self) -> char {
224        let mut rng = rand::thread_rng();
225        let rnd_index = rng.gen_range(0..=self.uppercase_char_set.len() - 1);
226
227        self.uppercase_char_set[rnd_index]
228    }
229
230    /// Returns a random number char
231    fn get_random_number(&self) -> char {
232        let mut rng = rand::thread_rng();
233        let rnd_index = rng.gen_range(0..=self.number_set.len() - 1);
234
235        self.number_set[rnd_index]
236    }
237
238    /// Returns a random special character char
239    fn get_random_special_character(&self) -> char {
240        let mut rng = rand::thread_rng();
241        let rnd_index = rng.gen_range(0..=self.spec_char_set.len() - 1);
242
243        self.spec_char_set[rnd_index]
244    }
245
246    /// Generates a random composition vec
247    fn generate_random_composition(&self) -> Vec<&CompositionCodes> {
248        let mut result: Vec<&CompositionCodes> = Vec::new();
249        let mut rng = rand::thread_rng();
250        let filtered_comp_codes = self
251            .composition_codes
252            .iter()
253            .filter(|code| match code {
254                CompositionCodes::Lowercase => {
255                    if self.uppercase_only {
256                        false
257                    } else {
258                        true
259                    }
260                }
261                CompositionCodes::Uppercase => {
262                    if self.lowercase_only {
263                        false
264                    } else {
265                        true
266                    }
267                }
268                CompositionCodes::Number => {
269                    if self.exclude_numbers {
270                        false
271                    } else {
272                        true
273                    }
274                }
275                CompositionCodes::SpecialCharacter => {
276                    if self.exclude_special_chars {
277                        false
278                    } else {
279                        true
280                    }
281                }
282            })
283            .collect::<Vec<_>>();
284
285        for _i in 0..self.length {
286            let rnd_index = rng.gen_range(0..=filtered_comp_codes.len() - 1);
287            let comp_char = filtered_comp_codes[rnd_index];
288
289            result.push(comp_char)
290        }
291
292        result
293    }
294
295    /// Generates a random string following the input composition vec
296    fn generate_random_string_from_composition(
297        &self,
298        composition: Vec<&CompositionCodes>,
299    ) -> String {
300        let mut password: Vec<char> = Vec::new();
301
302        for code in composition {
303            match code {
304                CompositionCodes::Lowercase => {
305                    let value = self.get_random_lowercase_char();
306                    password.push(value);
307                }
308                CompositionCodes::Uppercase => {
309                    let value = self.get_random_uppercase_char();
310                    password.push(value);
311                }
312                CompositionCodes::Number => {
313                    let value = self.get_random_number();
314                    password.push(value);
315                }
316                CompositionCodes::SpecialCharacter => {
317                    let value = self.get_random_special_character();
318                    password.push(value);
319                }
320            }
321        }
322
323        password.into_iter().collect()
324    }
325
326    /// Generates a password
327    ///
328    /// # Examples
329    ///
330    /// ```
331    /// use simple_password_generator::PasswordGenerator;
332    ///
333    /// let password = PasswordGenerator::new().generate();
334    /// ```
335    pub fn generate(&self) -> String {
336        let comp_code = self.generate_random_composition();
337        let password = self.generate_random_string_from_composition(comp_code);
338
339        password
340    }
341}