use rand::prelude::*;
use serde_derive::{Serialize, Deserialize};
const LOWERCASE: &str = "a b c d e f g h i j k l m n o p q r s t u v w x y z";
const UPPERCASE: &str = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z";
const SYMBOLS: &str = "! \" ; # $ % & ' ( ) * + , - . / : ; < = > ? @ [ ] ^ _ ` { | } ~";
const NUMBERS: &str = "1 2 3 4 5 6 7 8 9 0";
pub fn pick_symbol(dictionary: &str) -> String {
let dictionary: Vec<&str> = dictionary.split_whitespace().collect();
let mut range = thread_rng();
let dictionary_item = range.gen_range(0..dictionary.len());
dictionary[dictionary_item].to_owned()
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Generator {
pub lowercase: bool,
pub uppercase: bool,
pub symbols: bool,
pub numbers: bool,
pub begin_with_letter: bool,
pub length: usize,
pub category: String,
}
impl Generator {
pub fn new(length: usize) -> Self {
Self {
lowercase: false,
uppercase: false,
symbols: false,
numbers: false,
begin_with_letter: false,
length,
category: "undefined".to_string()
}
}
pub fn lowercase(mut self, lowercase: bool) -> Self {
self.lowercase = lowercase;
self
}
pub fn uppercase(mut self, uppercase: bool) -> Self {
self.uppercase = uppercase;
self
}
pub fn symbols(mut self, symbols: bool) -> Self {
self.symbols = symbols;
self
}
pub fn numbers(mut self, numbers: bool) -> Self {
self.numbers = numbers;
self
}
pub fn begin_with_letter(mut self, begin_with_letter: bool) -> Self {
self.begin_with_letter = begin_with_letter;
self
}
pub fn lenght(mut self, length: usize) -> Self {
self.length = length;
self
}
pub fn category(mut self, category: String) -> Self {
self.category = category;
self
}
pub fn generate_dictionary(&self) -> String {
let mut valid_string = String::new();
macro_rules! add_to_valid_string {
($opt:expr, $string:ident) => {
if $opt {
valid_string.push(' ');
valid_string.push_str($string);
}
};
}
add_to_valid_string!(self.lowercase, LOWERCASE);
add_to_valid_string!(self.uppercase, UPPERCASE);
add_to_valid_string!(self.symbols, SYMBOLS);
add_to_valid_string!(self.numbers, NUMBERS);
valid_string
}
pub fn generate(&self) -> Option<String> {
if self.length == 0 {
return None;
}
let mut password = String::new();
if self.begin_with_letter {
if !self.uppercase && !self.lowercase {
return None;
}
let temporary_options = Generator::new(0)
.uppercase(self.uppercase)
.lowercase(self.lowercase);
password.push_str(pick_symbol(&temporary_options.generate_dictionary()).as_str());
}
for _ in {
if self.begin_with_letter {
1
} else {
0
}
}..self.length {
password.push_str(pick_symbol(&self.generate_dictionary()).as_str());
}
Some(password)
}
}