ares/cli/mod.rs
1use std::{fs::File, io::Read};
2
3use crate::{cli_pretty_printing::panic_failure_both_input_and_fail_provided, config::Config};
4/// This doc string acts as a help message when the usees run '--help' in CLI mode
5/// as do all doc strings on fields
6use clap::Parser;
7use lemmeknow::Identifier;
8use log::trace;
9
10/// The struct for Clap CLI arguments
11#[derive(Parser)]
12#[command(author = "Bee <bee@skerritt.blog>", about, long_about = None)]
13pub struct Opts {
14 /// Some input. Because this isn't an Option<T> it's required to be used
15 #[arg(short, long)]
16 text: Option<String>,
17
18 /// A level of verbosity, and can be used multiple times
19 #[arg(short, long, action = clap::ArgAction::Count)]
20 verbose: u8,
21
22 /// Turn off human checker, perfect for APIs where you don't want input from humans
23 #[arg(short, long)]
24 disable_human_checker: bool,
25
26 /// Set timeout, if it is not decrypted after this time, it will return an error.
27 /// Default is 5 seconds.
28 // If we want to call it `timeout`, the short argument contends with the one for Text `ares -t`.
29 // I propose we just call it `cracking_timeout`.
30 #[arg(short, long)]
31 cracking_timeout: Option<u32>,
32 /// Run in API mode, this will return the results instead of printing them
33 /// Default is False
34 #[arg(short, long)]
35 api_mode: Option<bool>,
36 /// Opens a file for decoding
37 /// Use instead of `--text`
38 #[arg(short, long)]
39 file: Option<String>,
40 /// If you have a crib (you know a piece of information in the plaintext)
41 /// Or you want to create a custom regex to check against, you can use the Regex checker below.
42 /// This turns off other checkers (English, LemmeKnow)
43 #[arg(short, long)]
44 regex: Option<String>,
45}
46
47/// Parse CLI Arguments turns a Clap Opts struct, seen above
48/// Into a library Struct for use within the program
49/// The library struct can be found in the [config](../config) folder.
50/// # Panics
51/// This function can panic when it gets both a file and text input at the same time.
52pub fn parse_cli_args() -> (String, Config) {
53 let mut opts: Opts = Opts::parse();
54 let min_log_level = match opts.verbose {
55 0 => "Warn",
56 1 => "Info",
57 2 => "Debug",
58 _ => "Trace",
59 };
60 env_logger::init_from_env(
61 env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, min_log_level),
62 );
63
64 // If both the file and text are proivded, panic because we're not sure which one to use
65 if opts.file.is_some() && opts.text.is_some() {
66 panic_failure_both_input_and_fail_provided();
67 }
68
69 let input_text: String = if opts.file.is_some() {
70 read_and_parse_file(opts.file.unwrap())
71 } else {
72 opts.text
73 .expect("Error. No input was provided. Please use ares --help")
74 };
75
76 // Fixes bug where opts.text and opts.file are partially borrowed
77 opts.text = None;
78 opts.file = None;
79
80 trace!("Program was called with CLI 😉");
81 trace!("Parsed the arguments");
82 trace!("The inputted text is {}", &input_text);
83
84 cli_args_into_config_struct(opts, input_text)
85}
86
87/// When the CLI is called with `-f` to open a file
88/// this function opens it
89/// # Panics
90/// This can panic when opening a file which does not exist!
91pub fn read_and_parse_file(file_path: String) -> String {
92 // TODO pretty match on the errors to provide better output
93 // Else it'll panic
94 let mut file = File::open(file_path).unwrap();
95 let mut contents = String::new();
96 file.read_to_string(&mut contents).unwrap();
97 // We can just put the file into the `Opts.text` and the program will work as normal
98 // On Unix systems a line is defined as "\n{text}\n"
99 // https://stackoverflow.com/a/729795
100 // Which means if a user creates a file on Unix, it'll have a new line appended.
101 // This is probably not what they wanted to decode (it is not what I wanted) so we are removing them
102 if contents.ends_with(['\n', '\r']) {
103 contents.strip_suffix(['\n', '\r']).unwrap().to_owned()
104 } else {
105 contents
106 }
107}
108
109/// Turns our CLI arguments into a config stuct
110fn cli_args_into_config_struct(opts: Opts, text: String) -> (String, Config) {
111 (
112 text,
113 Config {
114 verbose: opts.verbose,
115 lemmeknow_config: Identifier::default(),
116 // default is false, we want default to be true
117 human_checker_on: !opts.disable_human_checker,
118 // These if statements act as defaults
119 timeout: if opts.cracking_timeout.is_none() {
120 30
121 } else {
122 opts.cracking_timeout.unwrap()
123 },
124 api_mode: opts.api_mode.is_some(),
125 regex: opts.regex,
126 },
127 )
128}