Skip to content

Commit

Permalink
Create printing module (#131)
Browse files Browse the repository at this point in the history
* Create printing module

This PR will create macros we can use to easily print things and control how things are printed, it is in draft state :)

* Updating pretty printing

* update doctest

* updating printing

* Cleaning up the general codebase while Im at it

* cargo clippy\
  • Loading branch information
bee-san authored Dec 11, 2022
1 parent 63df6ce commit 65d3214
Show file tree
Hide file tree
Showing 13 changed files with 189 additions and 161 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ bs58 = "0.4.0"
num = "0.4"
crossbeam = "0.8"
base65536 = "1.0.1"
ansi_term = "0.12.1"

[dev-dependencies]
criterion = "0.4.0"
Expand Down
10 changes: 3 additions & 7 deletions src/checkers/human_checker.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::checkers::checker_result::CheckResult;
use crate::cli_pretty_printing::human_checker_check;
use crate::config::get_config;
use crate::timer;
use text_io::read;
Expand All @@ -13,16 +14,11 @@ pub fn human_checker(input: &CheckResult) -> bool {
// wait instead of get so it waits for config being set
let config = get_config();
// We still call human checker, just if config is false we return True
if !config.human_checker_on {
if !config.human_checker_on || config.api_mode {
return true;
}
human_checker_check(&input.description, &input.text);

let output_string = format!(
"I think the plaintext is a {}.\nPossible plaintext: '{}' (y/N): ",
input.description, input.text
);
// print output_string and ask user to input yes or no
println!("{}", output_string);
let reply: String = read!("{}\n");
let result = reply.to_ascii_lowercase().starts_with('y');
if !result {
Expand Down
12 changes: 1 addition & 11 deletions src/checkers/lemmeknow_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,5 @@ impl Check for Checker<LemmeKnow> {
/// Formats the data result to a string
/// This is used to display the result in the UI
fn format_data_result(input: &Data) -> String {
/*
Input contains these:
println!("{}", input.name);
println!("{}", input.regex);
println!("{}", input.plural_name);
println!("{}", input.description);
println!("{}", input.rarity);
println!("{}", input.url);
println!("{:?}", input.tags);
In the future we'd want to include more advanced things like URL. */
input.name.to_string() // removed .to_string() from here
input.name.to_string()
}
17 changes: 13 additions & 4 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ pub struct Opts {
// If we want to call it `timeout`, the short argument contends with the one for Text `ares -t`.
// I propose we just call it `cracking_timeout`.
#[arg(short, long)]
cracking_timeout: u32,
cracking_timeout: Option<u32>,
/// Run in API mode, this will return the results instead of printing them
/// Default is False
#[arg(short, long)]
api_mode: Option<bool>,
}

/// Parse CLI Arguments turns a Clap Opts struct, seen above
Expand All @@ -43,6 +47,7 @@ pub fn parse_cli_args() -> (String, Config) {
env_logger::init_from_env(
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, min_log_level),
);

trace!("Program was called with CLI 😉");
trace!("Parsed the arguments");

Expand All @@ -58,9 +63,13 @@ fn cli_args_into_config_struct(opts: Opts) -> (String, Config) {
lemmeknow_config: Identifier::default(),
// default is false, we want default to be true
human_checker_on: !opts.disable_human_checker,
offline_mode: true,
// TODO make this into a CLI arg
timeout: opts.cracking_timeout,
// These if statements act as defaults
timeout: if opts.cracking_timeout.is_none() {
5
} else {
opts.cracking_timeout.unwrap()
},
api_mode: opts.api_mode.is_some(),
},
)
}
135 changes: 135 additions & 0 deletions src/cli_pretty_printing/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/// By having all of our print statements in one file it allows us to align what they look like
/// and make sure each one is up to our standards. Previously a rogue print statement that went off at an edge case
/// would look a bit ugly and not the same UI as others.
/// We can also do things like check for logic or share information / functions which would be a bit messy in the main code.
use crate::DecoderResult;

/// The output function is used to print the output of the program.
/// If the API mode is on, it will not print.
pub fn program_exiting_successful_decoding(result: DecoderResult) {
let config = crate::config::get_config();
if config.api_mode {
return;
}
let plaintext = result.text;
// calculate path
let decoded_path = result
.path
.iter()
.map(|c| c.decoder)
.collect::<Vec<_>>()
.join(" → ");

let decoded_path_string = if !decoded_path.contains('→') {
// handles case where only 1 decoder is used
format!("the decoder used is {}", decoded_path)
} else {
format!("the decoders used are {}", decoded_path)
};
println!(
"The plaintext is: \n{}\nand {}",
ansi_term::Colour::Yellow.bold().paint(&plaintext[0]),
decoded_path_string
);
}

/// The output function is used to print the output of the program.
pub fn decoded_how_many_times(depth: u32) {
let config = crate::config::get_config();
if config.api_mode {
return;
}

// Gets how many decoders we have
// Then we add 25 for Caesar, and roughly 25 for Binary
let decoders = crate::filtration_system::filter_and_get_decoders();
let decoded_times_int = depth * (decoders.components.len() as u32 + 25 + 25);
let decoded_times_str = ansi_term::Colour::Yellow
.bold()
.paint(format!("{} times", decoded_times_int));

let time_took = calculate_time_took(decoded_times_int);

// TODO add colour to the times
println!("\n🥳 Ares has decoded {} times.\nIf you would have used Ciphey, it would have taken you {}\n", decoded_times_str, time_took);
}

/// Whenever the human checker checks for text, this function is run.
/// The human checker checks to see if API mdoe is runnign inside of it
/// rather than doing it here at the printing level
pub fn human_checker_check(description: &str, text: &str) {
println!(
"🕵️ I think the plaintext is {}.\nPossible plaintext: '{}' (y/N): ",
ansi_term::Colour::Yellow.bold().paint(description),
ansi_term::Colour::Yellow.bold().paint(text)
)
}

/// When Ares has failed to decode something, print this message
pub fn failed_to_decode() {
let config = crate::config::get_config();
if config.api_mode {
return;
}
// The program can roughly do 45 decodings a second
// Currently it is not possible to get this info at this stage of the program from the decoding level
// TODO fix this
let ares_decodings = config.timeout * 45;
let time_took = calculate_time_took(ares_decodings);
println!("⛔️ Ares has failed to decode the text. If you would have used Ciphey, it would have taken you {}\n", time_took);
println!("If you want more help, please ask in #coded-messages in our Discord http://discord.skerritt.blog");
}
/// Calculate how long it would take to decode this in Ciphey
fn calculate_time_took(decoded_times_int: u32) -> String {
// TODO if we grab how long the programs been running for (see timer) we can make some nice stats like:
// * How many decodings / second we did
// * How much longer it'd take in Ciphey
// We'll guess Ciphey can do 8 a second. No science here, it's arbitrary based on my opinion
let ciphey_decodings_a_second = 5;
// Calculate how long it'd take in Ciphey
let ciphey_how_long_to_decode_in_seconds = decoded_times_int / ciphey_decodings_a_second;
if ciphey_how_long_to_decode_in_seconds > 60 {
// If it took
if ciphey_how_long_to_decode_in_seconds / 60 == 1 {
// Handle case where it's each 1 minute
// TODO 1 minutes is still broken for me
format!("{} minute", ciphey_how_long_to_decode_in_seconds / 60)
} else {
// 1.26 minutes sounds good in English
// So we do not need to handle special case here
format!("{} minutes", ciphey_how_long_to_decode_in_seconds / 60)
}
} else {
format!("{} seconds", ciphey_how_long_to_decode_in_seconds)
}
}

/// Every second the timer ticks once
/// If the timer hits our countdown, we exit the program.
/// This function prints the countdown to let the user know the program is still running.
pub fn countdown_until_program_ends(seconds_spent_running: u32, duration: u32) {
let config = crate::config::get_config();
if config.api_mode {
return;
}
if seconds_spent_running % 5 == 0 && seconds_spent_running != 0 {
let time_left = duration - seconds_spent_running;
if time_left == 0 {
return;
}
println!(
"{} seconds have passed. {} remaining",
seconds_spent_running, time_left
);
}
}

/// The input given to Ares is already plaintext
/// So we do not need to do anything
pub fn return_early_because_input_text_is_plaintext() {
let config = crate::config::get_config();
if config.api_mode {
return;
}
println!("Your input text is the plaintext 🥳");
}
11 changes: 6 additions & 5 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ use once_cell::sync::OnceCell;
/// It's access using a variable like configuration
/// ```rust
/// use ares::config::get_config;
/// // Assert that the CONFIG has an offline mode
/// let config = get_config();
/// assert!(!config.offline_mode);
/// assert_eq!(config.verbose, 0);
/// ```
pub struct Config {
Expand All @@ -23,11 +22,13 @@ pub struct Config {
/// Should the human checker be on?
/// This asks yes/no for plaintext. Turn off for API
pub human_checker_on: bool,
/// Is the program being run in offline mode?
pub offline_mode: bool,
/// The timeout threshold before Ares quites
/// This is in seconds
pub timeout: u32,
/// Is the program being run in API mode?
/// This is used to determine if we should print to stdout
/// Or return the values
pub api_mode: bool,
}

/// Cell for storing global Config
Expand Down Expand Up @@ -60,8 +61,8 @@ impl Default for Config {
verbose: 0,
lemmeknow_config: LEMMEKNOW_DEFAULT_CONFIG,
human_checker_on: false,
offline_mode: false,
timeout: 5,
api_mode: true,
}
}
}
5 changes: 5 additions & 0 deletions src/decoders/morse_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ fn normalise_morse_string(text: &str) -> String {

/// Maps morse code to its alphanumeric character, returns None for invalid morse-code
fn morse_to_alphanumeric(text: &str) -> Option<&str> {
trace!("Starting to map morse code to alphanumeric");
let result = match text {
".-" => "A",
"-..." => "B",
Expand Down Expand Up @@ -132,6 +133,10 @@ fn morse_to_alphanumeric(text: &str) -> Option<&str> {
"-.-.--" => "!",
" " => " ",
"" => "",
// Turns line breaks and new lines into space. This may break what the plaintext is supposed to be
// But enables us to support them
"\n" => " ",
"\r" => " ",
_ => return None,
};

Expand Down
9 changes: 3 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ pub mod checkers;
pub mod cli;
/// CLI Input Parser parses the input from the CLI and returns a struct.
mod cli_input_parser;
/// The CLI Pretty Printing module contains the functions that print the results
pub mod cli_pretty_printing;
/// The Config module enables a configuration module
/// Like a global API to access config details
pub mod config;
Expand Down Expand Up @@ -82,7 +84,7 @@ pub fn perform_cracking(text: &str, config: Config) -> Option<DecoderResult> {
"The input text provided to the program {} is the plaintext. Returning early.",
text
);
return_early_because_input_text_is_plaintext();
cli_pretty_printing::return_early_because_input_text_is_plaintext();
return None;
}

Expand All @@ -102,11 +104,6 @@ fn check_if_input_text_is_plaintext(text: &str) -> bool {
athena_checker.check(text).is_identified
}

/// A nice function to print when the input text is the plaintext
fn return_early_because_input_text_is_plaintext() {
println!("Your input text is the plaintext 🥳")
}

/// DecoderResult is the result of decoders
#[derive(Debug)]
pub struct DecoderResult {
Expand Down
15 changes: 3 additions & 12 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use ares::cli::parse_cli_args;
use ares::cli_pretty_printing::program_exiting_successful_decoding;
use ares::perform_cracking;

fn main() {
Expand All @@ -9,18 +10,8 @@ fn main() {
// TODO: As result have array of CrackResult used,
// we can print in better way with more info
Some(result) => {
println!("SUCCESSFUL 😁");
println!("PLAINTEXT: {:?}", result.text);
println!(
"DECODERS USED: {}",
result
.path
.iter()
.map(|c| c.decoder)
.collect::<Vec<_>>()
.join(" → ")
)
program_exiting_successful_decoding(result);
}
None => println!("FAILED 😭"),
None => ares::cli_pretty_printing::failed_to_decode(),
}
}
12 changes: 6 additions & 6 deletions src/searchers/bfs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::config::get_config;
use crate::{cli_pretty_printing::decoded_how_many_times, config::get_config};
use crossbeam::{channel::bounded, select};
use log::{error, trace};
use log::{debug, trace};
use std::collections::HashSet;

use crate::{filtration_system::MyResults, timer, DecoderResult};
Expand Down Expand Up @@ -94,14 +94,14 @@ pub fn bfs(input: &str) -> Option<DecoderResult> {
// but this is a demo and i'll be lazy :P
let exit_result = exit_result.ok(); // convert Result to Some
if exit_result.is_some() {
println!("Ares has decoded {times} times", times=curr_depth * (12+25));
trace!("Found exit result: {:?}", exit_result);
decoded_how_many_times(curr_depth);
debug!("Found exit result: {:?}", exit_result);
return exit_result;
}
},
recv(timer) -> _ => {
println!("Ares has decoded {times} times", times=curr_depth * (12+25));
error!("Ares failed to decrypt the text you have provided within {} seconds, it is unlikely to be decoded.", config.timeout);
decoded_how_many_times(curr_depth);
debug!("Ares has failed to decode");
return None;
},
default => continue,
Expand Down
Loading

0 comments on commit 65d3214

Please sign in to comment.