Modularize and add the pattern parameters

This commit is contained in:
runebaas 2019-10-30 22:23:20 +01:00
parent e3dc106eb7
commit 1d8f2a2815
No known key found for this signature in database
GPG key ID: 2677AF508D0300D6
10 changed files with 192 additions and 67 deletions

49
Cargo.lock generated
View file

@ -1,5 +1,13 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
@ -55,6 +63,7 @@ version = "0.1.0"
dependencies = [
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -67,11 +76,21 @@ dependencies = [
"wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ppv-lite86"
version = "0.2.6"
@ -114,6 +133,22 @@ dependencies = [
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "strsim"
version = "0.8.0"
@ -127,6 +162,14 @@ dependencies = [
"unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-width"
version = "0.1.6"
@ -162,6 +205,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
@ -169,14 +213,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8"
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412"
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd"
"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"

View file

@ -9,3 +9,4 @@ edition = "2018"
[dependencies]
rand = "0.7.2"
clap = "2.33.0"
regex = "1.3.1"

22
src/filters/length.rs Normal file
View file

@ -0,0 +1,22 @@
use crate::utils::options_parser::Parameters;
pub fn filter(fortunes: Vec<String>, options: &Parameters) -> Vec<String> {
let filter_fn = if options.long_fortunes {
filter_short
} else {
filter_long
};
fortunes
.into_iter()
.filter(|x| filter_fn(x, options.length))
.collect::<Vec<String>>()
}
fn filter_long(x: &str, max_length: usize) -> bool {
x.replace(" ", "").len() < max_length
}
fn filter_short(x: &str, max_length: usize) -> bool {
x.replace(" ", "").len() > max_length
}

2
src/filters/mod.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod length;
pub mod pattern;

22
src/filters/pattern.rs Normal file
View file

@ -0,0 +1,22 @@
use crate::utils::options_parser::Parameters;
use regex::Regex;
pub fn filter(fortunes: Vec<String>, params: &Parameters) -> Vec<String> {
let pattern = if params.case_insensitive {
format!("(?i){}", &params.pattern)
} else {
params.pattern.clone()
};
let regex = match Regex::new(&pattern) {
Ok(regex) => regex,
Err(err) => panic!(format!(
"Pattern \"{}\" is not a valid regex pattern: {}",
params.pattern, err
)),
};
fortunes
.into_iter()
.filter(|x| regex.is_match(x))
.collect::<Vec<String>>()
}

View file

@ -1,15 +1,9 @@
use std::{fs, thread, time, path, process};
use std::convert::TryFrom;
use rand::prelude::*;
use clap::{Arg, App};
mod filters;
mod utils;
struct Options {
filename: String,
length: usize,
long_fortunes: bool,
short_fortunes: bool,
wait: bool
}
use crate::utils::*;
use clap::{App, Arg};
use rand::prelude::*;
fn main() {
// specify app
@ -46,79 +40,47 @@ fn main() {
.arg(Arg::with_name("wait")
.short("w")
.long("wait")
.help("Wait before termination for an amount of time calculated from the number of characters in the message. (20 characters = 1 second, mim 6 seconds)"));
let options = parse_options(app);
.help("Wait before termination for an amount of time calculated from the number of characters in the message. (20 characters = 1 second, mim 6 seconds)"))
.arg(Arg::with_name("pattern")
.short("m")
.help("Print out all fortunes which match the basic regular expression pattern.")
.takes_value(true))
.arg(Arg::with_name("case_insensitive")
.short("i")
.help("Ignore case for -m patterns. ")
.takes_value(false));
let options = options_parser::parse(app);
// get fortunes
let mut fortunes = get_fortunes(options.filename.clone());
let mut fortunes = fortunes_reader::get_all(options.filename.clone());
// filter by max length
if options.short_fortunes || options.long_fortunes {
let filter_fn = if options.long_fortunes { filter_short } else { filter_long };
fortunes = fortunes
.into_iter()
.filter(|x| filter_fn(x, options.length))
.collect::<Vec<String>>();
fortunes = crate::filters::length::filter(fortunes, &options);
}
// apply the pattern
if options.pattern != String::new() {
fortunes = crate::filters::pattern::filter(fortunes, &options)
}
// get a random one
let the_fortune = get_random_fortune(fortunes);
// print it ✨
println!("{}", the_fortune);
if options.wait {
wait(the_fortune)
waiter::wait(the_fortune)
}
}
fn filter_long(x: &str, max_length: usize) -> bool {
return x.replace(" ", "").len() < max_length;
}
fn filter_short(x: &str, max_length: usize) -> bool {
return x.replace(" ", "").len() > max_length;
}
fn parse_options(app: App) -> Options {
let matches = app.get_matches();
let options: Options = Options {
filename: matches.value_of("file").unwrap().to_owned(),
length: matches.value_of("length").unwrap().parse::<usize>().expect("Length is not a valid number"),
short_fortunes: matches.is_present("short"),
long_fortunes: matches.is_present("long"),
wait: matches.is_present("wait")
};
return options;
}
fn get_fortunes(filename: String) -> Vec<String> {
if !path::Path::new(&filename).exists() {
println!("File '{}' does not found", filename);
process::exit(1);
}
let fortune_file = fs::read_to_string(filename).expect("Cannot read fortune file");
let fortunes: Vec<String> = fortune_file.split('%').map(ToOwned::to_owned).collect();
return fortunes;
}
fn get_random_fortune(fortunes: Vec<String>) -> String {
let total_fortunes = fortunes.len();
if total_fortunes == 0 {
return "No Fortunes found with these parameters...".to_owned();
}
let random_fortune = rand::thread_rng().gen_range(0, total_fortunes);
return fortunes.get(random_fortune).unwrap().trim().to_owned();
}
fn wait(fortune: String) {
let characters_per_second = 20; // as defined in the original fortune command
let minimum_wait = 6; // seconds
let mut time_to_wait = fortune.len() / characters_per_second;
if time_to_wait < minimum_wait {
time_to_wait = minimum_wait;
}
let fortune_length = u64::try_from(time_to_wait).unwrap();
let time_to_wait = time::Duration::from_secs(fortune_length);
thread::sleep(time_to_wait);
fortunes.get(random_fortune).unwrap().trim().to_owned()
}

View file

@ -0,0 +1,12 @@
use std::{fs, path, process};
pub fn get_all(filename: String) -> Vec<String> {
if !path::Path::new(&filename).exists() {
println!("File '{}' does not found", filename);
process::exit(1);
}
let fortune_file = fs::read_to_string(filename).expect("Cannot read fortune file");
let fortunes: Vec<String> = fortune_file.split('%').map(ToOwned::to_owned).collect();
fortunes
}

3
src/utils/mod.rs Normal file
View file

@ -0,0 +1,3 @@
pub mod fortunes_reader;
pub mod options_parser;
pub mod waiter;

View file

@ -0,0 +1,36 @@
use clap::App;
pub fn parse(app: App) -> Parameters {
let matches = app.get_matches();
let options: Parameters = Parameters {
filename: matches.value_of("file").unwrap().to_owned(),
length: matches
.value_of("length")
.unwrap()
.parse::<usize>()
.expect("Length is not a valid number"),
short_fortunes: matches.is_present("short"),
long_fortunes: matches.is_present("long"),
wait: matches.is_present("wait"),
pattern: match matches.value_of("pattern") {
Some(pat) => pat.to_owned(),
None => String::new(),
},
case_insensitive: matches.is_present("case_insensitive"),
};
options
}
pub struct Parameters {
pub filename: String,
pub length: usize,
pub long_fortunes: bool,
pub short_fortunes: bool,
pub wait: bool,
pub pattern: String,
pub case_insensitive: bool,
}

16
src/utils/waiter.rs Normal file
View file

@ -0,0 +1,16 @@
use std::convert::TryFrom;
use std::{thread, time};
pub fn wait(fortune: String) {
let characters_per_second = 20; // as defined in the original fortune command
let minimum_wait = 6; // seconds
let mut time_to_wait = fortune.len() / characters_per_second;
if time_to_wait < minimum_wait {
time_to_wait = minimum_wait;
}
let fortune_length = u64::try_from(time_to_wait).unwrap();
let time_to_wait = time::Duration::from_secs(fortune_length);
thread::sleep(time_to_wait);
}