Port over code from uwuify

pull/1/head
sgoudham 3 years ago
parent 808b7d8f8a
commit 876c6a8646

@ -0,0 +1,71 @@
use clap::{ArgGroup, ErrorKind, IntoApp, Parser};
use crate::uwuify::UwUify;
mod uwuify;
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
#[clap(group(ArgGroup::new("uwu").required(true).args(& ["text", "infile"]),))]
struct Args {
/// Text to uwu'ify
#[clap(short, long, required_unless_present_all = ["infile", "outfile"], display_order = 1)]
text: Option<String>,
/// The file to uwu'ify
#[clap(short, long, parse(from_os_str), conflicts_with = "text", requires = "outfile", value_name = "FILE", display_order = 2)]
infile: Option<std::path::PathBuf>,
/// The file to output uwu'ified text
#[clap(short, long, value_name = "FILE", display_order = 3)]
outfile: Option<String>,
/// The modifier to determine how many words to be uwu'ified
#[clap(short, long, value_name = "VALUE", default_value = "1", validator = is_between_zero_and_one, display_order = 4)]
words: f32,
/// The modifier for uwu faces e.g hello -> hewwo
#[clap(short, long, value_name = "VALUE", default_value = "0.05", validator = is_between_zero_and_one, display_order = 5)]
faces: f32,
/// The modifier for actions e.g *shuffles over*
#[clap(short, long, value_name = "VALUE", default_value = "0.125", validator = is_between_zero_and_one, display_order = 6)]
actions: f32,
/// The modifier for stutters e.g b-baka!
#[clap(short, long, value_name = "VALUE", default_value = "0.225", validator = is_between_zero_and_one, display_order = 7)]
stutters: f32,
/// Flag to enable/disable random uwu'ifying
#[clap(short, long, display_order = 8)]
random: bool,
}
fn main() {
let args = Args::parse();
let matches = Args::into_app().get_matches();
let supplied_at_runtime = modifiers_supplied_at_runtime(matches.occurrences_of("faces"), matches.occurrences_of("actions"), matches.occurrences_of("stutters"));
let uwuify = UwUify::new(args.text, args.infile, args.outfile, supplied_at_runtime, args.words, args.faces, args.actions, args.stutters, args.random);
match uwuify.uwuify() {
Ok(_) => (),
Err(err) => {
let mut app = Args::into_app();
app.error(ErrorKind::DisplayHelp, err.to_string()).exit();
}
}
}
fn is_between_zero_and_one(input: &str) -> Result<(), String> {
let value = match input.parse::<f32>() {
Ok(value) => value,
Err(_) => return Err(String::from("The value must be a decimal number"))
};
if (0.0..=1.0).contains(&value) { return Ok(()); }
Err(String::from("The value must be between 0.0 and 1.0"))
}
fn modifiers_supplied_at_runtime(faces_occurrences: u64, actions_occurrences: u64, stutters_occurrences: u64) -> bool {
faces_occurrences > 0 || actions_occurrences > 0 || stutters_occurrences > 0
}

@ -0,0 +1,203 @@
use std::io::Error;
use std::path::PathBuf;
use linkify::{LinkFinder, LinkKind};
use crate::uwuify::constants::ACTIONS;
use crate::uwuify::constants::ACTIONS_SIZE;
use crate::uwuify::constants::FACES;
use crate::uwuify::constants::FACES_SIZE;
use crate::uwuify::io::{UwUInFile, UwUOutFile};
use crate::uwuify::progress_bar::UwUProgressBar;
use crate::uwuify::seeder::UwUSeeder;
mod constants;
mod seeder;
mod progress_bar;
mod io;
#[derive(Debug)]
struct Modifiers {
supplied_at_runtime: bool,
words: f32,
faces: f32,
actions: f32,
stutters: f32,
}
#[derive(Debug)]
pub struct UwUify {
text: String,
input: PathBuf,
output: String,
modifiers: Modifiers,
random: bool,
linkify: LinkFinder,
}
impl UwUify {
pub fn new(text: Option<String>,
infile: Option<PathBuf>,
outfile: Option<String>,
supplied_at_runtime: bool,
words: f32,
faces: f32,
actions: f32,
stutters: f32,
random: bool) -> UwUify { // I dislike this
let mut linkify = LinkFinder::new();
linkify.kinds(&[LinkKind::Email, LinkKind::Url]);
linkify.url_must_have_scheme(false);
UwUify {
text: text.unwrap_or_default(),
input: infile.unwrap_or_default(),
output: outfile.unwrap_or_default(),
modifiers: Modifiers { supplied_at_runtime, words, faces, actions, stutters },
random,
linkify,
}
}
pub fn uwuify(&self) -> Result<(), Error> {
// Handle Text
if !self.text.is_empty() {
let uwu_text = self.uwuify_sentence(&self.text);
// Handle Text Output
if !self.output.is_empty() {
let mut uwu_out_file = match UwUOutFile::new(&self.output) {
Ok(uwu_out_file) => uwu_out_file,
Err(err) => return Err(err)
};
let mut uwu_progress_bar = UwUProgressBar::new(uwu_text.len() as u64);
match uwu_out_file.write_string(&uwu_text) {
Ok(_) => (),
Err(err) => return Err(err),
};
uwu_progress_bar.update_progess(uwu_text.len());
uwu_progress_bar.finish("UwU'ifying Complete!");
} else {
println!("{}", uwu_text);
}
} else {
// Handle File I/O
let mut uwu_in_file = match UwUInFile::new(&self.input) {
Ok(uwu_in_file) => uwu_in_file,
Err(err) => return Err(err),
};
let mut uwu_out_file = match UwUOutFile::new(&self.output) {
Ok(uwu_out_file) => uwu_out_file,
Err(err) => return Err(err)
};
let mut uwu_progress_bar = UwUProgressBar::new(uwu_in_file.get_file_bytes());
loop {
let bytes_read_in = match uwu_in_file.read_until_newline() {
Ok(bytes_read_in) => bytes_read_in,
Err(err) => return Err(err),
};
if bytes_read_in == 0 { break; }
let utf8_str = uwu_in_file.get_buffer_as_utf8_str();
let uwu_sentence = self.uwuify_sentence(&utf8_str);
match uwu_out_file.write_string_with_newline(&uwu_sentence) {
Ok(_) => (),
Err(err) => return Err(err),
};
uwu_progress_bar.update_progess(bytes_read_in);
uwu_in_file.clear_buffer();
}
uwu_progress_bar.finish("UwU'ifying Complete!");
}
Ok(())
}
fn uwuify_sentence(&self, text: &str) -> String {
text
.split_whitespace()
.map(|word| {
let uwu_word = self.uwuify_word(word.to_string());
self.uwuify_spaces(uwu_word)
})
.collect::<Vec<String>>()
.join(" ")
}
fn uwuify_word(&self, word: String) -> String {
if self.linkify.links(&word).count() > 0 {
return word;
}
let mut seeder = UwUSeeder::new(&word, self.random);
if seeder.random() > self.modifiers.words { return word; }
let word_bytes = word.as_bytes();
let uwu_text_count = word.len();
let mut uwu_text = String::new();
for index in 0..uwu_text_count {
let previous_previous_char = *word_bytes.get(index - 2).unwrap_or_else(|| &word_bytes[0]) as char;
let previous_char = *word_bytes.get(index - 1).unwrap_or_else(|| &word_bytes[0]) as char;
let current_char = word_bytes[index] as char;
match current_char {
'L' | 'R' => uwu_text.push('W'),
'l' | 'r' => uwu_text.push('w'),
'E' | 'e' => match previous_char {
'N' | 'n' => uwu_text.push_str(format!("y{}", current_char).as_str()),
'v' => match previous_previous_char {
'o' => {
uwu_text.pop();
uwu_text.pop();
uwu_text.push_str("uv");
}
_ => uwu_text.push(current_char)
}
_ => uwu_text.push(current_char)
}
'A' | 'I' | 'O' | 'U' | 'a' | 'i' | 'o' | 'u' => match previous_char {
'N' | 'n' => uwu_text.push_str(format!("y{}", current_char).as_str()),
_ => uwu_text.push(current_char)
}
_ => uwu_text.push(current_char)
}
}
uwu_text
}
fn uwuify_spaces(&self, mut word: String) -> String {
let mut seeder = UwUSeeder::new(&word, self.random);
let random_value = seeder.random();
if !self.modifiers.supplied_at_runtime {
if random_value <= self.modifiers.faces {
word = format!("{} {}", FACES[seeder.random_int(0, FACES_SIZE)], word);
} else if random_value <= self.modifiers.actions {
word = format!("{} {}", ACTIONS[seeder.random_int(0, ACTIONS_SIZE)], word);
} else if random_value <= self.modifiers.stutters {
let first_char_stutter = format!("{}-", word.chars().next().unwrap());
word = format!("{}{}", first_char_stutter.repeat(seeder.random_int(1, 2)), word);
}
} else {
if random_value <= self.modifiers.stutters {
let first_char_stutter = format!("{}-", word.chars().next().unwrap());
word = format!("{}{}", first_char_stutter.repeat(seeder.random_int(1, 2)), word);
}
if random_value <= self.modifiers.faces {
word = format!("{} {}", FACES[seeder.random_int(0, FACES_SIZE)], word);
}
if random_value <= self.modifiers.actions {
word = format!("{} {}", ACTIONS[seeder.random_int(0, ACTIONS_SIZE)], word);
}
}
word
}
}

@ -0,0 +1,39 @@
pub const FACES_SIZE: i32 = 15;
pub const FACES: [&str; FACES_SIZE as usize] = [
"OwO",
"UwU",
">w<",
"^w^",
"ÚwÚ",
"^-^",
":3",
"x3",
"xDD",
";;w;;",
">_<",
">_>",
"^.^",
":33",
"uWu",
];
pub const ACTIONS_SIZE: i32 = 17;
pub const ACTIONS: [&str; ACTIONS_SIZE as usize] = [
"*notices bulge*",
"*cries*",
"*hugs tightly*",
"*screams*",
"*looks away*",
"*blushes*",
"*sweats*",
"*cuddles you*",
"*moans*",
"*giggles shyly*",
"*looks at you*",
"*twerks*",
"*sighs*",
"*leans over*",
"*pokes you*",
"*teleports behind you*",
"*shuffles closer*",
];

@ -0,0 +1,80 @@
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Error, Write};
use std::path::{Path};
pub struct UwUInFile {
file_bytes: u64,
reader: BufReader<File>,
buffer: Vec<u8>,
}
pub struct UwUOutFile {
writer: BufWriter<File>,
}
impl UwUInFile {
pub fn new(path: &Path) -> Result<UwUInFile, Error> {
let file = match File::open(path) {
Ok(file) => file,
Err(err) => return Err(err)
};
let file_metadata = match file.metadata() {
Ok(file_metadata) => file_metadata,
Err(err) => return Err(err)
};
let file_bytes = file_metadata.len();
let reader = BufReader::new(file);
let buffer = Vec::new();
Ok(UwUInFile { file_bytes, reader, buffer })
}
pub fn read_until_newline(&mut self) -> Result<usize, Error> {
match self.reader.read_until(b'\n', &mut self.buffer) {
Ok(byte_vec) => Ok(byte_vec),
Err(err) => Err(err),
}
}
pub fn get_buffer_as_utf8_str(&self) -> String {
String::from_utf8_lossy(&self.buffer).to_string()
}
pub fn clear_buffer(&mut self) {
self.buffer.clear();
}
pub fn get_file_bytes(&self) -> u64 {
self.file_bytes
}
}
impl UwUOutFile {
pub fn new(path: &str) -> Result<UwUOutFile, Error> {
let file = match File::create(path) {
Ok(file) => file,
Err(err) => return Err(err)
};
let writer = BufWriter::new(file);
Ok(UwUOutFile { writer })
}
pub fn exists(path: &str) -> bool {
Path::new(path).exists()
}
pub fn write_string_with_newline(&mut self, write_str: &str) -> Result<(), Error> {
match self.writer.write_all(format!("{}\n", write_str).as_bytes()) {
Ok(_) => Ok(()),
Err(err) => Err(err),
}
}
pub fn write_string(&mut self, write_str: &str) -> Result<(), Error> {
match self.writer.write_all(write_str.as_bytes()) {
Ok(_) => Ok(()),
Err(err) => Err(err),
}
}
}

@ -0,0 +1,29 @@
use indicatif::{ProgressBar, ProgressStyle};
pub struct UwUProgressBar {
downloaded_bytes: u64,
progress_bar: ProgressBar,
}
impl UwUProgressBar {
pub fn new(file_total_bytes: u64) -> UwUProgressBar {
let progress_bar = ProgressBar::new(file_total_bytes);
progress_bar.set_style(ProgressStyle::default_bar()
.template("{spinner:.green} [{elapsed_precise}] [{bar:60.cyan/blue}] {bytes}/{total_bytes} ({eta}) {msg}")
.progress_chars("#>-"));
UwUProgressBar {
downloaded_bytes: 0,
progress_bar,
}
}
pub fn update_progess(&mut self, bytes_read_in: usize) {
self.downloaded_bytes += bytes_read_in as u64;
self.progress_bar.set_position(self.downloaded_bytes);
}
pub fn finish(&self, message: &'static str) {
self.progress_bar.finish_with_message(message);
}
}

@ -0,0 +1,35 @@
use rand::{Rng, rngs::ThreadRng, thread_rng};
use rand_pcg::Pcg32;
use rand_seeder::Seeder;
pub struct UwUSeeder {
seeder: Pcg32,
rng: ThreadRng,
random: bool,
}
impl UwUSeeder {
pub fn new(word: &str, random: bool) -> UwUSeeder {
UwUSeeder {
seeder: Seeder::from(word).make_rng(),
rng: thread_rng(),
random,
}
}
pub fn random(&mut self) -> f32 {
if self.random {
self.rng.gen_range(0.0..1.0)
} else {
self.seeder.gen_range(0.0..1.0)
}
}
pub fn random_int(&mut self, min: i32, max: i32) -> usize {
if self.random {
self.rng.gen_range(min..max) as usize
} else {
self.seeder.gen_range(min..max) as usize
}
}
}
Loading…
Cancel
Save