pash 0.1.0

Simple and easy app for generating and storing passwords
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";

// TODO: refactor to a struct method
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,
}
// TODO: rewrite these functions via macro
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)
    }
}