Skip to content

Commit

Permalink
refactor: improve project structure
Browse files Browse the repository at this point in the history
  • Loading branch information
sdanialraza committed Feb 28, 2024
1 parent 7f38c8a commit 8f9f1c1
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 135 deletions.
54 changes: 29 additions & 25 deletions src/random_password.rs → src/app.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,40 @@
use rand::{distributions::Uniform, thread_rng, Rng};

use crate::constants::SPECIAL_CHARACTERS;

#[derive(Copy, Clone)]
pub struct RandomPasswordOptions {
pub length: u8,
pub include_lowercase: bool,
pub include_uppercase: bool,
pub include_numbers: bool,
pub include_special_characters: bool,
}
use crate::{
constants::SPECIAL_CHARACTERS,
types::{PasswordGeneratorOptions, RandomPassword},
};

impl Default for RandomPasswordOptions {
impl Default for PasswordGeneratorOptions {
fn default() -> Self {
Self {
length: 10,
include_lowercase: true,
include_uppercase: true,
include_numbers: true,
include_special_characters: true,
include_uppercase: true,
length: 10,
}
}
}

pub struct RandomPassword {
pub characters: Vec<char>,
pub struct PasswordGenerator {
pub options: PasswordGeneratorOptions,
pub password: String,
pub range: Uniform<usize>,
pub recent_passwords: Vec<String>,
}

impl Default for PasswordGenerator {
fn default() -> Self {
Self {
options: PasswordGeneratorOptions::default(),
password: RandomPassword::new(PasswordGeneratorOptions::default()).password,
recent_passwords: Vec::with_capacity(10),
}
}
}

impl RandomPassword {
pub fn new(options: RandomPasswordOptions) -> Self {
pub fn new(options: PasswordGeneratorOptions) -> Self {
let mut password = String::new();
let mut characters: Vec<char> = Vec::with_capacity(86);

Expand Down Expand Up @@ -72,11 +76,11 @@ impl RandomPassword {

#[cfg(test)]
mod tests {
use super::{RandomPassword, RandomPasswordOptions};
use super::{PasswordGeneratorOptions, RandomPassword};

#[test]
fn it_generates_lowercase_only_password() {
let options = RandomPasswordOptions {
let options = PasswordGeneratorOptions {
include_lowercase: true,
include_uppercase: false,
include_numbers: false,
Expand All @@ -93,7 +97,7 @@ mod tests {

#[test]
fn it_generates_uppercase_only_password() {
let options = RandomPasswordOptions {
let options = PasswordGeneratorOptions {
include_lowercase: false,
include_uppercase: true,
include_numbers: false,
Expand All @@ -110,7 +114,7 @@ mod tests {

#[test]
fn it_generates_numbers_only_password() {
let options = RandomPasswordOptions {
let options = PasswordGeneratorOptions {
include_lowercase: false,
include_uppercase: false,
include_numbers: true,
Expand All @@ -127,7 +131,7 @@ mod tests {

#[test]
fn it_generates_special_characters_only_password() {
let options = RandomPasswordOptions {
let options = PasswordGeneratorOptions {
include_lowercase: false,
include_uppercase: false,
include_numbers: false,
Expand All @@ -144,7 +148,7 @@ mod tests {

#[test]
fn it_generates_lower_upper_numbers_special_characters_password() {
let options = RandomPasswordOptions::default();
let options = PasswordGeneratorOptions::default();

let random_password = RandomPassword::new(options);

Expand All @@ -157,9 +161,9 @@ mod tests {

#[test]
fn it_generates_random_password_with_length() {
let options = RandomPasswordOptions {
let options = PasswordGeneratorOptions {
length: 30,
..RandomPasswordOptions::default()
..PasswordGeneratorOptions::default()
};

let random_password = RandomPassword::new(options);
Expand Down
7 changes: 7 additions & 0 deletions src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
/// Application id and name
pub const APP_ID: &str = "password-generator";
pub const APP_NAME: &str = "Password Generator";

/// Maximum password length
pub const MAX_PASSWORD_LENGTH: u8 = 50;

/// Special characters for random password generation
pub const SPECIAL_CHARACTERS: [char; 14] = ['!', '@', '#', '$', '%', '&', '^', '&', '(', ')', '[', ']', '-', '+'];
91 changes: 91 additions & 0 deletions src/gui.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use copypasta::{ClipboardContext, ClipboardProvider};
use eframe::{
egui::{CentralPanel, Context, RichText, Slider},
App, Frame,
};
use serde_json::{from_str, json};
use std::time::Duration;

use crate::{app::PasswordGenerator, constants::MAX_PASSWORD_LENGTH, types::RandomPassword};

impl App for PasswordGenerator {
fn auto_save_interval(&self) -> Duration {
Duration::from_secs(5)
}

fn save(&mut self, storage: &mut dyn eframe::Storage) {
let mut passwords_to_add: Vec<String> = Vec::with_capacity(10);

passwords_to_add.push(self.password.clone());

let mut recent_passwords = match storage.get_string("recent_passwords") {
Some(passwords) => {
let passwords: Vec<_> = from_str(passwords.as_str()).unwrap_or_default();

passwords_to_add.extend(passwords);

passwords_to_add
}
None => passwords_to_add,
};

recent_passwords.sort();

recent_passwords.dedup();

storage.set_string("recent_passwords", json!(recent_passwords).to_string());
}

fn update(&mut self, ctx: &Context, _frame: &mut Frame) {
CentralPanel::default().show(ctx, |ui| {
ui.label(RichText::new(self.password.to_string()).size(18.0));

ui.add(Slider::new(&mut self.options.length, 1..=MAX_PASSWORD_LENGTH).text("Length"));

ui.checkbox(&mut self.options.include_lowercase, "Include Lowercase");
ui.checkbox(&mut self.options.include_uppercase, "Include Uppercase");
ui.checkbox(&mut self.options.include_numbers, "Include Numbers");
ui.checkbox(
&mut self.options.include_special_characters,
"Include Special Characters",
);

let checkbox_options = [
self.options.include_lowercase,
self.options.include_uppercase,
self.options.include_numbers,
self.options.include_special_characters,
];

if ui.button(RichText::new("Generate")).clicked() {
if !checkbox_options.iter().any(|&x| x) {
return self.password = String::from("At least one of checkboxes should be checked");
}

self.password = RandomPassword::new(self.options).password;

if self.recent_passwords.len() >= 10 {
self.recent_passwords.pop();
}

self.recent_passwords.push(self.password.clone());
}

if ui.button(RichText::new("Copy")).clicked() {
let mut context = ClipboardContext::new().unwrap();

let _copied = context.set_contents(self.password.to_string());
}

ui.separator();

ui.heading("Recent Passwords");

ui.vertical(|ui| {
for password in self.recent_passwords.iter().rev() {
ui.label(RichText::new(password.to_string()).size(14.0));
}
});
});
}
}
121 changes: 11 additions & 110 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

mod constants;
mod random_password;

use constants::MAX_PASSWORD_LENGTH;
use random_password::{RandomPassword, RandomPasswordOptions};
use eframe::{egui::ViewportBuilder, run_native, Error, NativeOptions};
use serde_json::from_str;

use std::time::Duration;
mod app;
mod constants;
mod gui;
mod types;

use copypasta::{ClipboardContext, ClipboardProvider};
use eframe::{
egui::{CentralPanel, Context, RichText, Slider, ViewportBuilder},
run_native, App, Error, Frame, NativeOptions,
use crate::{
app::PasswordGenerator,
constants::{APP_ID, APP_NAME},
};
use serde_json::{from_str, json};

fn main() -> Result<(), Error> {
let options = NativeOptions {
centered: true,
follow_system_theme: true,
viewport: ViewportBuilder::default()
.with_app_id("password-generator")
.with_app_id(APP_ID)
.with_inner_size([300.0, 200.0]),
..Default::default()
};

run_native(
"Password Generator",
APP_NAME,
options,
Box::new(|creation_context| {
let storage = match creation_context.storage {
Expand All @@ -46,100 +44,3 @@ fn main() -> Result<(), Error> {
}),
)
}
struct PasswordGenerator {
options: RandomPasswordOptions,
password: String,
recent_passwords: Vec<String>,
}

impl Default for PasswordGenerator {
fn default() -> Self {
Self {
options: RandomPasswordOptions::default(),
password: RandomPassword::new(RandomPasswordOptions::default()).password,
recent_passwords: Vec::with_capacity(10),
}
}
}

impl App for PasswordGenerator {
fn save(&mut self, storage: &mut dyn eframe::Storage) {
let mut passwords_to_add: Vec<String> = Vec::with_capacity(10);

passwords_to_add.push(self.password.clone());

let mut recent_passwords = match storage.get_string("recent_passwords") {
Some(passwords) => {
let passwords: Vec<_> = from_str(passwords.as_str()).unwrap_or_default();

passwords_to_add.extend(passwords);

passwords_to_add
}
None => passwords_to_add,
};

recent_passwords.sort();

recent_passwords.dedup();

storage.set_string("recent_passwords", json!(recent_passwords).to_string());
}

fn update(&mut self, ctx: &Context, _frame: &mut Frame) {
CentralPanel::default().show(ctx, |ui| {
ui.label(RichText::new(self.password.to_string()).size(18.0));

ui.add(Slider::new(&mut self.options.length, 1..=MAX_PASSWORD_LENGTH).text("Length"));

ui.checkbox(&mut self.options.include_lowercase, "Include Lowercase");
ui.checkbox(&mut self.options.include_uppercase, "Include Uppercase");
ui.checkbox(&mut self.options.include_numbers, "Include Numbers");
ui.checkbox(
&mut self.options.include_special_characters,
"Include Special Characters",
);

let checkbox_options = [
self.options.include_lowercase,
self.options.include_uppercase,
self.options.include_numbers,
self.options.include_special_characters,
];

if ui.button(RichText::new("Generate")).clicked() {
if !checkbox_options.iter().any(|&x| x) {
return self.password = String::from("At least one of checkboxes should be checked");
}

self.password = RandomPassword::new(self.options).password;

if self.recent_passwords.len() >= 10 {
self.recent_passwords.pop();
}

self.recent_passwords.push(self.password.clone());
}

if ui.button(RichText::new("Copy")).clicked() {
let mut context = ClipboardContext::new().unwrap();

let _copied = context.set_contents(self.password.to_string());
}

ui.separator();

ui.heading("Recent Passwords");

ui.vertical(|ui| {
for password in self.recent_passwords.iter().rev() {
ui.label(RichText::new(password.to_string()).size(14.0));
}
});
});
}

fn auto_save_interval(&self) -> std::time::Duration {
Duration::from_secs(5)
}
}
16 changes: 16 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use rand::distributions::Uniform;

#[derive(Copy, Clone)]
pub struct PasswordGeneratorOptions {
pub include_lowercase: bool,
pub include_numbers: bool,
pub include_special_characters: bool,
pub include_uppercase: bool,
pub length: u8,
}

pub struct RandomPassword {
pub characters: Vec<char>,
pub password: String,
pub range: Uniform<usize>,
}

0 comments on commit 8f9f1c1

Please sign in to comment.