Skip to content

Commit

Permalink
refactor(memorable-pass): update ui-cli
Browse files Browse the repository at this point in the history
  • Loading branch information
DaRacci committed Sep 25, 2023
1 parent 8da3a2c commit f282a43
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 238 deletions.
12 changes: 6 additions & 6 deletions crates/memorable-pass/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ macros = { path = "../macros" }

# Runtimes
tokio = { workspace = true }
#tokio-stream = { workspace = true }

# Cli & UI
clap = { workspace = true }
Expand All @@ -37,12 +36,13 @@ serde_json = { workspace = true }
rust-embed = { workspace = true }

# Logging & Errors
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
#thiserror = { workspace = true }
anyhow = { workspace = true }
tracing.workspace = true
tracing-subscriber.workspace = true
tracing-appender.workspace = true
#thiserror.workspace = true
anyhow.workspace = true

derivative = { version = "2.2.0", features = [] }
derivative = { version = "2.2.0" }

[dev-dependencies]
# Logging & Errors
Expand Down
13 changes: 4 additions & 9 deletions crates/memorable-pass/src/config/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,19 @@ use anyhow::Context;
use rust_embed::RustEmbed;
use std::collections::HashMap;
use std::sync::LazyLock;
use tracing::{debug, error, info};
use tracing::instrument;

pub static WORDS: LazyLock<HashMap<usize, Vec<String>>> = LazyLock::new(get_words);

#[derive(RustEmbed)]
#[folder = "assets"]
pub struct Asset;

#[instrument(level = "TRACE", ret)]
fn get_words() -> HashMap<usize, Vec<String>> {
let start = std::time::Instant::now();

let asset_file =
Asset::get("words.json").context("Find words.json asset file.").expect("Failed to find words.json asset file.");
let words_map = serde_json::from_slice::<HashMap<usize, Vec<String>>>(&asset_file.data)
serde_json::from_slice::<HashMap<usize, Vec<String>>>(&asset_file.data)
.context("Parse words.json asset into Map")
.expect("Failed to parse words.json asset into Map");

debug!("Loaded words in {}ms", start.elapsed().as_millis());

words_map
.expect("Failed to parse words.json asset into Map")
}
72 changes: 22 additions & 50 deletions crates/memorable-pass/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,34 @@
use crate::config::asset::WORDS;
use rand::Rng;
use tracing::{instrument, trace};
use crate::processor::processor::Processor;
use crate::rules::rule::Rule;
use crate::rules::rules::Rules;

pub mod config;
pub mod processor;
pub mod rules;

#[cfg(windows)]
const CONF_PATH: &str = "%APPDATA%\\.config\\pgen\\rules.toml";
#[cfg(unix)]
const CONF_PATH: &str = "~/.config/pgen/rules.toml";
pub mod ui;

pub type TransformationFn = impl Fn(&str) -> String;

pub async fn generate(rules: &Rules) -> Vec<String> {
let mut passwords = Vec::with_capacity(rules.amount);

// TODO :: Spawn a thread for each password.
while passwords.len() < rules.amount {
let words = random_words(rules.word_count, rules.word_length_min, rules.word_length_max).await;
let mut processor = Processor::new(words);
rules.addition_digits.process(&mut processor);
rules.addition_separator.process(&mut processor);
rules.transformation_case.process(&mut processor);

passwords.push(processor.finish());
}

passwords
}

// TODO :: Turn into stream
#[instrument(level = "TRACE")]
pub async fn random_words<'a>(word_count: u8, word_length_min: u8, word_length_max: u8) -> Vec<&'a str> {
Expand All @@ -45,55 +61,11 @@ pub async fn random_words<'a>(word_count: u8, word_length_min: u8, word_length_m
trace!("Finding {} words within range {:?}", word_count, range);
while words.len() < word_count as usize {
let length = seed.gen_range(range.clone());
let possible_words = (&WORDS).get(&(length as usize)).unwrap();
let possible_words = WORDS.get(&(length as usize)).unwrap();
let word = possible_words.get(seed.gen_range(0..possible_words.len())).unwrap().as_str();

words.push(word);
}

words
}

// #[derive(Debug, Subcommand)]
// pub enum Commands {
// Generate {
// #[command(flatten)]
// rules: Rules,
//
// /// The file to use as the rules config.
// #[arg(short, long, default_value_t = CONF_PATH.into())]
// file: String,
//
// #[command(flatten)]
// flags: lib::cli::Flags,
// },
// Config {
// #[command(subcommand)]
// action: ConfigAction,
// },
// }
//
// #[derive(Debug, Subcommand)]
// pub enum ConfigAction {
// Generate {
// #[arg(short, long, default_value_t = CONF_PATH.into())]
// file: String,
//
// #[command(flatten)]
// rules: Rules,
//
// /// Whether to overwrite the file if it already exists.
// #[arg(short, long)]
// force: bool,
//
// #[command(flatten)]
// flags: lib::cli::Flags,
// },
// Show {
// #[arg(short, long, default_value_t = CONF_PATH.into())]
// file: String,
//
// #[command(flatten)]
// flags: lib::cli::Flags,
// },
// }
174 changes: 6 additions & 168 deletions crates/memorable-pass/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,178 +17,16 @@
#![feature(exclusive_range_pattern)]
#![feature(async_fn_in_trait)]

use clap::{Parser, Subcommand};
use lib::cli::Flags;
use lib::ui::cli::cli::{AsyncCliUI, CliResult, CliUI};
use memorable_pass::processor::processor::Processor;
use memorable_pass::rules::rule::Rule;
use memorable_pass::rules::rules::Rules;
use memorable_pass::{config, random_words};
use tokio::runtime::Handle;
use tracing::{debug, info};

#[derive(Debug, Parser)]
#[command(name = env!["CARGO_PKG_NAME"], version, author, about)]
pub struct CliOneShot {
#[command(flatten)]
pub flags: Flags,

#[command(flatten)]
pub rules: Rules,

#[arg(long)]
pub repl: bool,
}

#[derive(Debug, Parser)]
#[command(name = env!["CARGO_PKG_NAME"], version, author, about)]
pub struct CliRepl {
#[command(flatten)]
pub flags: Flags,

#[command(subcommand)]
pub action: ReplCommand,
}

#[derive(Debug, Subcommand)]
pub enum ReplCommand {
Generate,
Rules(Rules),

Ping,
#[command(alias = "q")]
Quit,
}

pub struct App {
rules: Option<Rules>,
}

impl CliUI for App {
type OneShotCommand = CliOneShot;
type ReplCommand = CliRepl;

fn new(_args: Self::Args) -> CliResult<Self>
where
Self: Sized,
{
// Preload the words
Handle::current().spawn(async {
let _preload = &config::asset::WORDS;
});

Ok(Self { rules: None })
}
}

impl AsyncCliUI for App {
async fn handle_command(&mut self, command: Self::OneShotCommand) -> CliResult<()> {
self.rules.replace(command.rules);
let passwords = generate(self.rules.as_ref().unwrap()).await;
info!(
"Generated passwords:\n\n{passwords}\n",
passwords = passwords.join("\n")
);

Ok(())
}

async fn handle_repl_command(&mut self, command: Self::ReplCommand) -> CliResult<bool> {
match command.action {
ReplCommand::Generate => {
let rules = self.rules.get_or_insert_with(|| {
debug!("No rules set, using defaults");
Rules::default()
});

let passwords = generate(&rules).await;
info!(
"Generated passwords:\n\n{passwords}\n",
passwords = passwords.join("\n")
);
}
ReplCommand::Rules(rules) => {
let previous_rules = self.rules.replace(rules);

if let Some(previous_rules) = previous_rules {
debug!("Replacing previous rules.");
debug!("Previous rules:\n\n{previous_rules:?}\n");
}
}
ReplCommand::Ping => {
info!("Pong!");
}
ReplCommand::Quit => {
info!("Quitting...");
return Ok(true);
}
}

Ok(false)
}
}

async fn generate(rules: &Rules) -> Vec<String> {
let mut passwords = Vec::with_capacity(rules.amount);
while passwords.len() < rules.amount {
let words = random_words(rules.word_count, rules.word_length_min, rules.word_length_max).await;
let mut processor = Processor::new(words);
rules.addition_digits.process(&mut processor);
rules.addition_separator.process(&mut processor);
rules.transformation_case.process(&mut processor);

passwords.push(processor.finish());
}

passwords
}
use anyhow::Result;
use lib::ui::cli::cli::{AsyncCliUI, CliUI};
use memorable_pass::ui::cli::ui::MemorablePassCli;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut app = App::new(())?;
let (repl, verbosity) = CliOneShot::try_parse().map(|cli| (cli.repl, cli.flags.verbose))?;
let _guard = lib::log::init(env!("CARGO_PKG_NAME"), verbosity);

match repl {
true => app.repl().await,
false => app.run().await,
}?;

// let cli = Cli::parse();
// let _guard = lib::log::init(env!("CARGO_PKG_NAME"), cli.flags.verbose);
//
// debug!("CLI: {:#?}", cli);
//
// let mut passwords = Vec::with_capacity(cli.rules.amount);
// while passwords.len() < cli.rules.amount {
// let words = random_words(
// cli.rules.word_count,
// cli.rules.word_length_min,
// cli.rules.word_length_max,
// )
// .await;
// let mut processor = Processor::new(words);
// cli.rules.addition_digits.process(&mut processor);
// cli.rules.addition_separator.process(&mut processor);
// cli.rules.transformation_case.process(&mut processor);
//
// passwords.push(processor.finish());
// }
//
// info!(
// "Generated passwords:\n\n{passwords}\n",
// passwords = passwords.join("\n")
// );
async fn main() -> Result<()> {
let mut cli = MemorablePassCli::new(())?;
cli.run().await?;

// match cli.command {
// Commands::Generate { flags, file, rules } => {
// let _guard = lib::log::init("PGen", flags.verbose);
// let rules = merge_rules(rules, PathBuf::from(file));
// let mut generator = Generator::new(*rules)?;
// let passwords = generator.generate().join("\n");
//
// info!("Generated passwords:\n\n{passwords}\n");
// }
// Commands::Config { action } => match action {
// ConfigAction::Show { flags, file } => {
// let _guard = lib::log::init("PGen-Config-Show", flags.verbose);
Expand Down
4 changes: 2 additions & 2 deletions crates/memorable-pass/src/processor/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ impl<'a> Processor<'a> {
},
}
}
Action::Transformation(_, condition, transformation) => {
Action::Transformation(_, _condition, transformation) => {
let before = mut_word.len();
let ranged = &mut_word[start_end.0..start_end.1];
mut_word.replace_range(start_end.0..start_end.1, &*transformation(ranged));
mut_word.replace_range(start_end.0..start_end.1, &transformation(ranged));
start_end = (
start_end.0 + (mut_word.len() - before),
start_end.1 + (mut_word.len() - before),
Expand Down
4 changes: 2 additions & 2 deletions crates/memorable-pass/src/rules/addition/separator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ mod tests {
.process_with_passable(&mut processor, &mut Some('?'));

let result = processor.finish();
assert_eq!("^hello?world?$", result);
assert_eq!("hello?world?", result);
}

#[test]
Expand All @@ -154,7 +154,7 @@ mod tests {
let group = "[!@$%\\.&*\\-+=?:]";
println!("{}", result);
assert_matches!(
Regex::new(&*format!("^hello{group}world{group}$")).unwrap().is_match(&result),
Regex::new(&format!("^hello{group}world{group}$")).unwrap().is_match(&result),
true
);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/memorable-pass/src/rules/transformation/case.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ impl Rule for CaseTransformation {
_last: bool,
_passable: &mut Self::Passable,
) -> Vec<Action> {
let copy = self.clone();
let copy = *self;
vec![Action::Transformation(Priority::High, ActionCondition::Always, move |str| match &copy {
CaseTransformation::None => str.to_string(),
CaseTransformation::Uppercase => str.to_ascii_uppercase(),
Expand Down
Loading

0 comments on commit f282a43

Please sign in to comment.