use std::ops::Range;
const ZERO_TO_NINE: Range<usize> = 48..57;
const UCASE_AZ: Range<usize> = 65..91;
const LCASE_AZ: Range<usize> = 97..123;
const LATIN_1: Range<usize> = 192..247;
const LATIN_2: Range<usize> = 248..256;
const MISC_CHARS: &[&i32] = &[&131, &138, &140, &142, &154, &156, &158, &159];
pub mod utils {
use atoi::atoi;
use rand::{rng, seq::SliceRandom};
use unicode_segmentation::UnicodeSegmentation;
use crate::utilities::{LATIN_1, LATIN_2, LCASE_AZ, MISC_CHARS, UCASE_AZ, ZERO_TO_NINE};
fn get_all_valid_ascii_chars() -> Vec<usize> {
let zero_to_nine: Vec<usize> = ZERO_TO_NINE.collect();
let ucase_az: Vec<usize> = UCASE_AZ.collect();
let lcase_az: Vec<usize> = LCASE_AZ.collect();
let latin1: Vec<usize> = LATIN_1.collect();
let latin2: Vec<usize> = LATIN_2.collect();
let misc_chars: Vec<usize> = MISC_CHARS.iter().map(|&x| *x as usize).collect();
[zero_to_nine, ucase_az, lcase_az, latin1, latin2, misc_chars].concat()
}
fn get_valid_end_index(s: &str, valid_chars: &[usize]) -> usize {
let mut ret: usize = 0;
let trimmed: &str = s.trim();
let g: Vec<&str> = trimmed.graphemes(true).collect::<Vec<&str>>();
for (index, character) in g.iter().rev().enumerate() {
let bytes: &[u8] = character.as_bytes();
let first_byte: usize = bytes[0] as usize;
if character.is_ascii() && valid_chars.contains(&first_byte) {
ret = index;
break;
}
}
g.len() - ret - 1
}
fn get_valid_start_index(s: &str, valid_chars: &[usize]) -> usize {
let mut ret: usize = 0;
let trimmed: &str = s.trim();
let g: Vec<&str> = trimmed.graphemes(true).collect::<Vec<&str>>();
for (index, character) in g.iter().enumerate() {
let bytes: &[u8] = character.as_bytes();
let first_byte: usize = bytes[0] as usize;
if character.is_ascii() && valid_chars.contains(&first_byte) {
ret = index;
break;
}
}
ret
}
fn has_apostrophes(s: &str) -> bool {
let g: Vec<&str> = s.graphemes(true).collect::<Vec<&str>>();
let mut it: std::slice::Iter<'_, &str> = g.iter();
let index: Option<usize> = it.position(|&r| r == "'");
index.is_some()
}
fn has_hyphens(s: &str) -> bool {
let g: Vec<&str> = s.graphemes(true).collect::<Vec<&str>>();
let mut it: std::slice::Iter<'_, &str> = g.iter();
let index: Option<usize> = it.position(|&r| r == "-");
index.is_some()
}
fn handle_apostrophe_string(s: &str) -> String {
let mut v: Vec<String> = Vec::new();
let it: std::str::Split<'_, &str> = s.split("'");
for part in it {
v.push(scramble_word(part.to_owned()));
}
v.join("'")
}
fn handle_hyphenated_string(s: &str) -> String {
let mut coll: Vec<String> = Vec::new();
let it: std::str::Split<'_, &str> = s.split("-");
for part in it {
coll.push(scramble_word(part.to_owned()));
}
coll.join("-")
}
fn handle_apostrophe_and_hyphenated_string(s: &str) -> String {
let mut v1: Vec<String> = Vec::new();
let mut v2: Vec<String> = Vec::new();
let it: std::str::Split<'_, &str> = s.split("-");
for i in it {
let st = i.split("'");
for s in st {
v2.push(scramble_word(s.to_owned()));
}
v1.push(v2.join("'"));
v2.clear();
}
v1.join("-")
}
fn is_numeric_string(s: &str) -> bool {
let atoi_str: Option<u64> = atoi::<u64>(s.as_bytes());
atoi_str.is_some()
}
pub fn scramble_word(s: String) -> String {
let input_as_str: &str = s.as_str();
let valid_chars: Vec<usize> = get_all_valid_ascii_chars();
let g: Vec<&str> = input_as_str.graphemes(true).collect::<Vec<&str>>();
if has_apostrophes(&s) && has_hyphens(&s) {
return handle_apostrophe_and_hyphenated_string(&s);
}
if has_apostrophes(&s) {
return handle_apostrophe_string(&s);
}
if has_hyphens(&s) {
return handle_hyphenated_string(&s);
}
if g.len() <= 3 || g.len() > 15 || is_numeric_string(s.as_str()) {
return s;
}
let start_index = get_valid_start_index(input_as_str, &valid_chars);
let end_index = get_valid_end_index(input_as_str, &valid_chars);
if start_index == end_index {
return s;
}
let first = &g[0..=start_index];
let middle = &g[start_index + 1..end_index];
let last = &g[end_index..];
let mut mtv = middle.to_vec();
mtv.shuffle(&mut rng());
let middle_scrambled = &mtv[..];
let concatenated = [first, middle_scrambled, last].concat();
concatenated.join("")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_numeric_string() {
let lst1 = ["hello", " ", "_123"];
for item in lst1.iter() {
let result = is_numeric_string(item);
assert_eq!(result, false);
}
let lst2 = ["12345", "3.1415", "12/22/1986", "36-26-36"];
for item in lst2.iter() {
let result = is_numeric_string(item);
assert_eq!(result, true);
}
}
#[test]
fn test_get_valid_start_index() {
let all_valid_ascii = get_all_valid_ascii_chars();
let mut map: std::collections::HashMap<&'static str, usize> =
std::collections::HashMap::new();
map.insert("hello", 0usize);
map.insert(" hello", 0usize); map.insert("__hello", 2usize);
map.insert("❤️ to everyone", 2usize);
for (word, index) in map.iter() {
assert_eq!(get_valid_start_index(word, &all_valid_ascii), *index);
}
}
#[test]
fn test_get_valid_end_index() {
let all_valid_ascii = get_all_valid_ascii_chars();
let mut map: std::collections::HashMap<&'static str, usize> =
std::collections::HashMap::new();
map.insert("hello", 4usize);
map.insert("__hello", 6usize);
map.insert("__ hello", 7usize);
map.insert("to everyone❤️", 10usize);
for (word, index) in map.iter() {
assert_eq!(get_valid_end_index(word, &all_valid_ascii), *index);
}
}
#[test]
fn test_has_apostrophes() {
let lst1 = ["doesn't", "won't", "couldn't", "O'Shag-hennesey"];
for item in lst1.iter() {
let result = has_apostrophes(item);
assert_eq!(result, true);
}
let lst2 = ["foo", "bar", "baz"];
for item in lst2.iter() {
let result = has_apostrophes(item);
assert_eq!(result, false);
}
}
#[test]
fn test_single_apostrophe_string() {
let s: &'static str = "O'Shaghennessy"; let result: String = handle_apostrophe_string(s);
let parts: Vec<&str> = result.split("'").collect();
let first_word: &&str = parts.get(0).unwrap();
let first_word_grapheme: Vec<&str> = first_word.graphemes(true).collect::<Vec<&str>>();
let second_word: &&str = parts.get(1).unwrap();
let second_word_grapheme: Vec<&str> =
second_word.graphemes(true).collect::<Vec<&str>>();
let first_word_first_char: &str = *first_word_grapheme.get(0).unwrap();
let second_word_first_char: &str = *second_word_grapheme.get(0).unwrap();
let second_word_last_char: &str = *second_word_grapheme
.get(second_word_grapheme.len() - 1)
.unwrap();
assert_eq!(first_word_first_char, "O");
assert_eq!(second_word_first_char, "S");
assert_eq!(second_word_last_char, "y");
}
#[test]
fn test_double_apostrophe_string() {
let s: &'static str = "woulda'coulda'shoulda";
let result: String = handle_apostrophe_string(s);
let parts: Vec<&str> = result.split("'").collect();
let first_word: &&str = parts.get(0).unwrap();
let first_word_grapheme: Vec<&str> = first_word.graphemes(true).collect::<Vec<&str>>();
let second_word: &&str = parts.get(1).unwrap();
let second_word_grapheme: Vec<&str> =
second_word.graphemes(true).collect::<Vec<&str>>();
let third_word: &&str = parts.get(2).unwrap();
let third_word_grapheme: Vec<&str> = third_word.graphemes(true).collect::<Vec<&str>>();
let first_word_first_char: &str = *first_word_grapheme.get(0).unwrap();
let first_word_last_char: &str = *first_word_grapheme
.get(first_word_grapheme.len() - 1)
.unwrap();
let second_word_first_char: &str = *second_word_grapheme.get(0).unwrap();
let second_word_last_char: &str = *second_word_grapheme
.get(second_word_grapheme.len() - 1)
.unwrap();
let third_word_first_char: &str = *third_word_grapheme.get(0).unwrap();
let third_word_last_char: &str = *third_word_grapheme
.get(third_word_grapheme.len() - 1)
.unwrap();
assert_eq!(first_word_first_char, "w"); assert_eq!(first_word_last_char, "a"); assert_eq!(second_word_first_char, "c"); assert_eq!(second_word_last_char, "a"); assert_eq!(third_word_first_char, "s"); assert_eq!(third_word_last_char, "a"); }
#[test]
fn test_has_hyphens() {
let lst1 = ["Spanish-speaking", "all-or-nothing", "dipsy-doo-dunkaroo"];
for item in lst1.iter() {
let result = has_hyphens(item);
assert_eq!(result, true);
}
let lst2 = ["Spanish", "all", "dipsy"];
for item in lst2.iter() {
let result = has_hyphens(item);
assert_eq!(result, false);
}
}
#[test]
fn test_single_hyphen_string() {
let s: &'static str = "nitty-gritty";
let result: String = handle_hyphenated_string(s);
let parts: Vec<&str> = result.split("-").collect();
let first_word: &&str = parts.get(0).unwrap();
let first_word_grapheme: Vec<&str> = first_word.graphemes(true).collect::<Vec<&str>>();
let second_word: &&str = parts.get(1).unwrap();
let second_word_grapheme: Vec<&str> =
second_word.graphemes(true).collect::<Vec<&str>>();
let first_word_first_char: &str = *first_word_grapheme.get(0).unwrap();
let first_word_last_char: &str = *first_word_grapheme
.get(first_word_grapheme.len() - 1)
.unwrap();
let second_word_first_char: &str = *second_word_grapheme.get(0).unwrap();
let second_word_last_char: &str = *second_word_grapheme
.get(second_word_grapheme.len() - 1)
.unwrap();
assert_eq!(first_word_first_char, "n");
assert_eq!(first_word_last_char, "y");
assert_eq!(second_word_first_char, "g");
assert_eq!(second_word_last_char, "y");
}
#[test]
fn test_double_hyphen_string() {
let s: &'static str = "over-the-counter";
let result: String = handle_hyphenated_string(s);
let parts: Vec<&str> = result.split("-").collect();
let first_word: &&str = parts.get(0).unwrap();
let first_word_grapheme: Vec<&str> = first_word.graphemes(true).collect::<Vec<&str>>();
let second_word: &&str = parts.get(1).unwrap();
let second_word_grapheme: Vec<&str> =
second_word.graphemes(true).collect::<Vec<&str>>();
let third_word: &&str = parts.get(2).unwrap();
let third_word_grapheme: Vec<&str> = third_word.graphemes(true).collect::<Vec<&str>>();
let first_word_first_char: &str = *first_word_grapheme.get(0).unwrap();
let first_word_last_char: &str = *first_word_grapheme
.get(first_word_grapheme.len() - 1)
.unwrap();
let second_word_first_char: &str = *second_word_grapheme.get(0).unwrap();
let second_word_last_char: &str = *second_word_grapheme
.get(second_word_grapheme.len() - 1)
.unwrap();
let third_word_first_char: &str = *third_word_grapheme.get(0).unwrap();
let third_word_last_char: &str = *third_word_grapheme
.get(third_word_grapheme.len() - 1)
.unwrap();
assert_eq!(first_word_first_char, "o"); assert_eq!(first_word_last_char, "r"); assert_eq!(second_word_first_char, "t"); assert_eq!(second_word_last_char, "e"); assert_eq!(third_word_first_char, "c"); assert_eq!(third_word_last_char, "r"); }
#[test]
fn test_triple_hyphen_string() {
let s: &'static str = "head-in-the-clouds";
let result: String = handle_hyphenated_string(s);
let parts: Vec<&str> = result.split("-").collect();
assert_eq!(parts.get(1), Some("in").as_ref());
assert_eq!(parts.get(2), Some("the").as_ref());
let fourth_word: &&str = parts.get(3).unwrap();
assert_eq!(fourth_word.chars().nth(0), Some('c'));
assert_eq!(fourth_word.chars().nth(5), Some('s'));
}
#[test]
fn test_dont_scramble_short_words() {
let mut map: std::collections::HashMap<String, String> =
std::collections::HashMap::new();
map.insert(String::from(""), String::from(""));
map.insert(String::from("a"), String::from("a"));
map.insert(String::from("the"), String::from("the"));
map.insert(String::from("for"), String::from("for"));
map.insert(String::from("and"), String::from("and"));
map.insert(String::from("12/22/1986"), String::from("12/22/1986"));
for (word, matcher) in map.iter() {
assert_eq!(scramble_word(word.to_string()), matcher.to_string());
}
}
#[test]
fn test_dont_scramble_long_words() {
let mut map: std::collections::HashMap<String, String> =
std::collections::HashMap::new();
map.insert(
String::from("antidisestablishmentarianism"),
String::from("antidisestablishmentarianism"),
);
map.insert(
String::from("anthropomorphism"),
String::from("anthropomorphism"),
);
map.insert(
String::from("unconstitutional"),
String::from("unconstitutional"),
);
map.insert(
String::from("multidimensional"),
String::from("multidimensional"),
);
for (word, matcher) in map.iter() {
assert_eq!(scramble_word(word.to_string()), matcher.to_string());
}
}
#[test]
fn test_one_valid_ascii_char() {
let mut map: std::collections::HashMap<String, String> =
std::collections::HashMap::new();
map.insert(String::from("_a"), String::from("_a"));
map.insert(String::from("a_"), String::from("a_"));
map.insert(String::from("___a___"), String::from("___a___"));
map.insert(String::from("a...,,,"), String::from("a...,,,"));
map.insert(String::from("!@#$%a"), String::from("!@#$%a"));
for (word, matcher) in map.iter() {
assert_eq!(scramble_word(word.to_string()), matcher.to_string());
}
}
}
}