use std::borrow::Cow; use lazy_static::lazy_static; use regex::Regex; fn to_title_case<'a>(text: &'a str) -> Cow<'a, str> { let mut chars = text.chars(); match chars.next() { None => Cow::Borrowed(text), Some(c) => Cow::Owned( c.to_uppercase().collect::() + &chars.as_str().to_lowercase() ) } } lazy_static! { static ref FACE_RE: Regex = Regex::new(r"[:Xx][\)\]|p|P|d|D|o|O]").unwrap(); } trait Trollify { fn sentance_delimiter(&self) -> char { '.' } fn troll_word<'w>(&self, word: &'w str) -> Cow<'w, str> { Cow::Borrowed(word) } fn troll_line<'l>(&self, line: &'l str) -> Cow<'l, str> { Cow::Borrowed(line) } fn troll_sentence<'s>(&self, sentence: &'s str) -> Cow<'s, str> { Cow::Borrowed(sentence) } fn troll_all<'a>(&self, all: &'a str) -> Cow<'a, str> { Cow::Borrowed(all) } fn troll_face<'f>(&self, face: &'f str) -> Cow<'f, str> { Cow::Borrowed(face) } } struct Araida; impl Trollify for Araida { fn troll_word<'w>(&self, word: &'w str) -> Cow<'w, str> { Cow::Owned(word.to_lowercase().replace("o", "0")) } } struct Tavros; impl Trollify for Tavros { fn sentance_delimiter(&self) -> char { ',' } fn troll_sentence<'s>(&self, sentence: &'s str) -> Cow<'s, str> { let mut chars = sentence.chars(); match chars.next() { None => Cow::Borrowed(sentence), Some(c) => Cow::Owned( c.to_lowercase().collect::() + &chars.as_str().to_uppercase() ) } } fn troll_all<'a>(&self, all: &'a str) -> Cow<'a, str> { Cow::Owned(all .replace(".", ",") .replace("?", ",") .replace("!", ",") ) } fn troll_face<'f>(&self, face: &'f str) -> Cow<'f, str> { Cow::Owned(String::from("}") + face) } } struct Sollux; impl Trollify for Sollux { fn troll_word<'w>(&self, word: &'w str) -> Cow<'w, str> { Cow::Owned(word .to_lowercase() .replace("s", "2") .replace("i", "ii") .replace("to", "two") ) } } struct Karkat; impl Trollify for Karkat { fn troll_all<'a>(&self, all: &'a str) -> Cow<'a, str> { Cow::Owned(all.to_uppercase()) } } struct Nepeta; impl Trollify for Nepeta { fn troll_line<'l>(&self, line: &'l str) -> Cow<'l, str> { Cow::Owned( String::from(":33 < ") + &line.to_lowercase() ) } } struct Kanaya; impl Trollify for Kanaya { fn troll_word<'w>(&self, word: &'w str) -> Cow<'w, str> { to_title_case(word) } } struct Terezi; impl Trollify for Terezi { fn troll_word<'w>(&self, word: &'w str) -> Cow<'w, str> { Cow::Owned(word .to_uppercase() .replace("A", "4") .replace("I", "1") .replace("E", "3") ) } fn troll_face<'f>(&self, face: &'f str) -> Cow<'f, str> { Cow::Owned( String::from(">") + &face.replace(")", "]") ) } } struct Vriska; impl Trollify for Vriska { fn troll_word<'w>(&self, word: &'w str) -> Cow<'w, str> { Cow::Owned(word.replace("b", "8")) } fn troll_sentence<'s>(&self, sentence: &'s str) -> Cow<'s, str> { to_title_case(sentence) } fn troll_face<'f>(&self, face: &'f str) -> Cow<'f, str> { Cow::Owned(face .replace(":", "::::") .replace("X", "XXXX") ) } } struct Equius; impl Trollify for Equius { fn troll_word<'w>(&self, word: &'w str) -> Cow<'w, str> { Cow::Owned(word.replace("x", "%")) } fn troll_sentence<'s>(&self, sentence: &'s str) -> Cow<'s, str> { to_title_case(sentence) } fn troll_line<'l>(&self, line: &'l str) -> Cow<'l, str> { Cow::Owned(String::from("D --> ") + line) } } struct Gamzee; impl Trollify for Gamzee { fn troll_all<'a>(&self, all: &'a str) -> Cow<'a, str> { let chars = all.chars(); let mut next = true; let mut buf = String::new(); for char in chars { if char.is_alphabetic() { if next { buf += &char.to_uppercase().to_string(); } else { buf += &char.to_lowercase().to_string(); } next = !next; } else { buf.push(char); } } Cow::Owned(buf) } fn troll_face<'f>(&self, face: &'f str) -> Cow<'f, str> { Cow::Owned(face.replace(":", ":o")) } } struct Eridan; impl Trollify for Eridan { fn troll_word<'w>(&self, word: &'w str) -> Cow<'w, str> { Cow::Owned(word .to_lowercase() .replace("v", "vv") .replace("w", "ww") ) } } struct Feferi; impl Trollify for Feferi { fn troll_word<'w>(&self, word: &'w str) -> Cow<'w, str> { Cow::Owned(word .replace("h", ") (") .replace("E", "-E") ) } fn troll_sentence<'s>(&self, sentence: &'s str) -> Cow<'s, str> { to_title_case(sentence) } fn troll_face<'f>(&self, face: &'f str) -> Cow<'f, str> { Cow::Owned(face.replace(":", "3:")) } } #[derive(clap::ValueEnum, serde::Serialize, Clone, Copy, Debug)] #[serde(rename_all = "kebab-case")] pub enum Troll { Araida, Tavros, Sollux, Karkat, Nepeta, Kanaya, Terezi, Vriska, Equius, Gamzee, Eridan, Feferi } fn get_troll(troll: Troll) -> Box { match troll { Troll::Araida => Box::new(Araida {}), Troll::Tavros => Box::new(Tavros {}), Troll::Sollux => Box::new(Sollux {}), Troll::Karkat => Box::new(Karkat {}), Troll::Nepeta => Box::new(Nepeta {}), Troll::Kanaya => Box::new(Kanaya {}), Troll::Terezi => Box::new(Terezi {}), Troll::Vriska => Box::new(Vriska {}), Troll::Equius => Box::new(Equius {}), Troll::Gamzee => Box::new(Gamzee {}), Troll::Eridan => Box::new(Eridan {}), Troll::Feferi => Box::new(Feferi {}), } } pub fn trollify<'a>(text: &'a str, troll: Troll) -> String { let troll = get_troll(troll); let text = troll.troll_all(text); let sentences = text.split(troll.sentance_delimiter()); let mut buf = String::new(); for sentence in sentences { let sentence = troll.troll_sentence(sentence); sentence .split(" ") .into_iter() .map(|word| { match FACE_RE.is_match(word) { true => troll.troll_face(word), false => troll.troll_word(word) } }) .enumerate() .for_each(|(idx, word)| { if idx != 0 { buf += " "; } buf += &word; }); } let text = buf; let lines = text.split("\n"); let mut buf = String::new(); for (idx, line) in lines.enumerate() { if idx != 0 { buf.push('\n'); } let line = troll.troll_line(line); buf += &line; } let text = buf; text }