Speewd *UwU*

pull/2/head
Isaac Mills 3 years ago
parent 6be7aea531
commit 29292bd1c9
No known key found for this signature in database
GPG Key ID: B67D7410F33A0F61

@ -24,7 +24,12 @@ name = "uwuify"
[dependencies] [dependencies]
clap = { version = "3.0.10", features = ["derive"] } clap = { version = "3.0.10", features = ["derive"] }
rand = "0.8.4" rand = "0.8.4"
rand_seeder = "0.2.2"
rand_pcg = "0.3.1"
indicatif = "0.16.2" indicatif = "0.16.2"
linkify = "0.8.0" linkify = "0.8.0"
ahash = "0.7.6"
rand_xoshiro = "0.6.0"
[profile.release]
lto = "fat"
codegen-units = 1
panic = "abort"

File diff suppressed because it is too large Load Diff

@ -1,24 +1,11 @@
pub const FACES_SIZE: i32 = 15; pub const FACES_SIZE: usize = 15;
pub const FACES: [&str; FACES_SIZE as usize] = [ pub const FACES: [&str; FACES_SIZE] = [
"OwO", "OwO", "UwU", ">w<", "^w^", "ÚwÚ", "^-^", ":3", "x3", "xDD", ";;w;;", ">_<", ">_>", "^.^",
"UwU", ":33", "uWu",
">w<",
"^w^",
"ÚwÚ",
"^-^",
":3",
"x3",
"xDD",
";;w;;",
">_<",
">_>",
"^.^",
":33",
"uWu",
]; ];
pub const ACTIONS_SIZE: i32 = 17; pub const ACTIONS_SIZE: usize = 17;
pub const ACTIONS: [&str; ACTIONS_SIZE as usize] = [ pub const ACTIONS: [&str; ACTIONS_SIZE] = [
"*notices bulge*", "*notices bulge*",
"*cries*", "*cries*",
"*hugs tightly*", "*hugs tightly*",
@ -36,4 +23,4 @@ pub const ACTIONS: [&str; ACTIONS_SIZE as usize] = [
"*pokes you*", "*pokes you*",
"*teleports behind you*", "*teleports behind you*",
"*shuffles closer*", "*shuffles closer*",
]; ];

@ -5,80 +5,57 @@ use std::path::Path;
pub struct UwUInFile { pub struct UwUInFile {
file_bytes: u64, file_bytes: u64,
reader: BufReader<File>, reader: BufReader<File>,
buffer: Vec<u8>, pub buffer: String,
} }
pub struct UwUOutFile { #[repr(transparent)]
writer: BufWriter<File>, pub struct UwUOutFile<T: Write> {
writer: BufWriter<T>,
} }
impl UwUInFile { impl UwUInFile {
#[inline]
pub fn new(path: &Path) -> Result<UwUInFile, Error> { pub fn new(path: &Path) -> Result<UwUInFile, Error> {
let file = match File::open(path) { let file = 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 { Ok(UwUInFile {
file_bytes, file_bytes: file.metadata()?.len(),
reader, reader: BufReader::new(file),
buffer, buffer: String::new(),
}) })
} }
#[inline]
pub fn read_until_newline(&mut self) -> Result<usize, Error> { pub fn read_until_newline(&mut self) -> Result<usize, Error> {
match self.reader.read_until(b'\n', &mut self.buffer) { self.reader.read_line(&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()
} }
#[inline]
pub fn clear_buffer(&mut self) { pub fn clear_buffer(&mut self) {
self.buffer.clear(); self.buffer.clear();
} }
#[inline]
pub fn get_file_bytes(&self) -> u64 { pub fn get_file_bytes(&self) -> u64 {
self.file_bytes self.file_bytes
} }
} }
impl UwUOutFile { impl<T: Write> UwUOutFile<T> {
pub fn new(path: &str) -> Result<UwUOutFile, Error> { #[inline]
let file = match File::create(path) { pub fn new(writer: T) -> UwUOutFile<T> {
Ok(file) => file, UwUOutFile {
Err(err) => return Err(err), writer: BufWriter::new(writer),
}; }
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> { #[inline]
match self.writer.write_all(format!("{}\n", write_str).as_bytes()) { pub fn write_newline(&mut self) -> Result<(), Error> {
Ok(_) => Ok(()), self.writer.write_all(b"\n")
Err(err) => Err(err),
}
} }
#[inline]
pub fn write_string(&mut self, write_str: &str) -> Result<(), Error> { pub fn write_string(&mut self, write_str: &str) -> Result<(), Error> {
match self.writer.write_all(write_str.as_bytes()) { self.writer.write_all(write_str.as_bytes())
Ok(_) => Ok(()),
Err(err) => Err(err),
}
} }
} }

@ -1,5 +1,7 @@
use linkify::{LinkFinder, LinkKind}; use linkify::{LinkFinder, LinkKind};
use std::error::Error; use std::error::Error;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
use constants::ACTIONS; use constants::ACTIONS;
@ -18,10 +20,10 @@ mod seeder;
#[derive(Debug)] #[derive(Debug)]
struct Modifiers { struct Modifiers {
supplied_at_runtime: bool, supplied_at_runtime: bool,
words: f32, words: f64,
faces: f32, faces: f64,
actions: f32, actions: f64,
stutters: f32, stutters: f64,
} }
#[derive(Debug)] #[derive(Debug)]
@ -40,10 +42,10 @@ impl UwUify {
infile: Option<PathBuf>, infile: Option<PathBuf>,
outfile: Option<String>, outfile: Option<String>,
supplied_at_runtime: bool, supplied_at_runtime: bool,
words: f32, words: f64,
faces: f32, faces: f64,
actions: f32, actions: f64,
stutters: f32, stutters: f64,
random: bool, random: bool,
) -> UwUify { ) -> UwUify {
// I dislike this // I dislike this
@ -71,11 +73,9 @@ impl UwUify {
pub fn uwuify(&self) -> Result<(), Box<dyn Error>> { pub fn uwuify(&self) -> Result<(), Box<dyn Error>> {
// Handle Text // Handle Text
if !self.text.is_empty() { if !self.text.is_empty() {
let uwu_text = self.uwuify_sentence(&self.text);
// Handle Text Output // Handle Text Output
if !self.output.is_empty() { if !self.output.is_empty() {
if UwUOutFile::exists(&self.output) { if std::path::Path::new(&self.output).exists() {
return Err(format!( return Err(format!(
"File '{}' already exists, aborting operation", "File '{}' already exists, aborting operation",
&self.output &self.output
@ -83,78 +83,67 @@ impl UwUify {
.into()); .into());
} }
let mut uwu_out_file = match UwUOutFile::new(&self.output) { let mut uwu_out_file = UwUOutFile::new(File::create(&self.output)?);
Ok(uwu_out_file) => uwu_out_file, self.uwuify_sentence(&self.text, &mut uwu_out_file)?;
Err(err) => return Err(err.into()),
};
let mut uwu_progress_bar = UwUProgressBar::new(uwu_text.len() as u64);
match uwu_out_file.write_string(&uwu_text) { let mut uwu_progress_bar = UwUProgressBar::new(self.text.len() as u64);
Ok(_) => (),
Err(err) => return Err(err.into()),
};
uwu_progress_bar.update_progess(uwu_text.len()); uwu_progress_bar.update_progess(self.text.len());
uwu_progress_bar.finish("UwU'ifying Complete!"); uwu_progress_bar.finish("UwU'ifying Complete!");
Ok(())
} else { } else {
println!("{}", uwu_text); let stdout = std::io::stdout();
let mut out = UwUOutFile::new(stdout.lock());
self.uwuify_sentence(&self.text, &mut out)?;
out.write_newline()?;
Ok(())
} }
} else { } else {
// Handle File I/O // Handle File I/O
if UwUOutFile::exists(&self.output) { if std::path::Path::new(&self.output).exists() {
return Err( return Err(
format!("File '{}' already exists, aborting operation", &self.output).into(), format!("File '{}' already exists, aborting operation", &self.output).into(),
); );
}; }
let mut uwu_in_file = match UwUInFile::new(&self.input) { let mut uwu_in_file = UwUInFile::new(&self.input)?;
Ok(uwu_in_file) => uwu_in_file, let mut uwu_out_file = UwUOutFile::new(File::create(&self.output)?);
Err(err) => return Err(err.into()),
};
let mut uwu_out_file = match UwUOutFile::new(&self.output) {
Ok(uwu_out_file) => uwu_out_file,
Err(err) => return Err(err.into()),
};
let mut uwu_progress_bar = UwUProgressBar::new(uwu_in_file.get_file_bytes()); let mut uwu_progress_bar = UwUProgressBar::new(uwu_in_file.get_file_bytes());
loop { loop {
let bytes_read_in = match uwu_in_file.read_until_newline() { let bytes_read_in = uwu_in_file.read_until_newline()?;
Ok(bytes_read_in) => bytes_read_in, if bytes_read_in != 0 {
Err(err) => return Err(err.into()), self.uwuify_sentence(&uwu_in_file.buffer, &mut uwu_out_file)?;
}; uwu_out_file.write_newline()?;
if bytes_read_in == 0 {
break; uwu_progress_bar.update_progess(bytes_read_in);
uwu_in_file.clear_buffer();
} else {
uwu_progress_bar.finish("UwU'ifying Complete!");
return Ok(());
} }
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.into()),
};
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 { fn uwuify_sentence<T: Write>(
&self,
text: &str,
out: &mut UwUOutFile<T>,
) -> Result<(), std::io::Error> {
text.split_whitespace() text.split_whitespace()
.map(|word| { .map(|word| {
let uwu_word = self.uwuify_word(word.to_string()); let uwu_word = self.uwuify_word(word.to_string());
self.uwuify_spaces(uwu_word) self.uwuify_spaces(uwu_word)
}) })
.collect::<Vec<String>>() .try_for_each(|f| {
.join(" ") out.write_string(&f)?;
out.write_string(" ")
})
} }
fn uwuify_word(&self, word: String) -> String { fn uwuify_word(&self, word: String) -> String {
use std::fmt::Write;
if self.linkify.links(&word).count() > 0 { if self.linkify.links(&word).count() > 0 {
return word; return word;
} }
@ -179,7 +168,9 @@ impl UwUify {
'L' | 'R' => uwu_text.push('W'), 'L' | 'R' => uwu_text.push('W'),
'l' | 'r' => uwu_text.push('w'), 'l' | 'r' => uwu_text.push('w'),
'E' | 'e' => match previous_char { 'E' | 'e' => match previous_char {
'N' | 'n' => uwu_text.push_str(format!("y{}", current_char).as_str()), 'N' | 'n' => uwu_text
.write_fmt(format_args!("y{}", current_char))
.unwrap(),
'v' => match previous_previous_char { 'v' => match previous_previous_char {
'o' => { 'o' => {
uwu_text.pop(); uwu_text.pop();
@ -191,7 +182,9 @@ impl UwUify {
_ => uwu_text.push(current_char), _ => uwu_text.push(current_char),
}, },
'A' | 'I' | 'O' | 'U' | 'a' | 'i' | 'o' | 'u' => match previous_char { 'A' | 'I' | 'O' | 'U' | 'a' | 'i' | 'o' | 'u' => match previous_char {
'N' | 'n' => uwu_text.push_str(format!("y{}", current_char).as_str()), 'N' | 'n' => uwu_text
.write_fmt(format_args!("y{}", current_char))
.unwrap(),
_ => uwu_text.push(current_char), _ => uwu_text.push(current_char),
}, },
_ => uwu_text.push(current_char), _ => uwu_text.push(current_char),
@ -207,14 +200,14 @@ impl UwUify {
if !self.modifiers.supplied_at_runtime { if !self.modifiers.supplied_at_runtime {
if random_value <= self.modifiers.faces { if random_value <= self.modifiers.faces {
word = format!("{} {}", FACES[seeder.random_int(0, FACES_SIZE)], word); word = format!("{} {}", FACES[seeder.random_int(0..FACES_SIZE)], word);
} else if random_value <= self.modifiers.actions { } else if random_value <= self.modifiers.actions {
word = format!("{} {}", ACTIONS[seeder.random_int(0, ACTIONS_SIZE)], word); word = format!("{} {}", ACTIONS[seeder.random_int(0..ACTIONS_SIZE)], word);
} else if random_value <= self.modifiers.stutters { } else if random_value <= self.modifiers.stutters {
let first_char_stutter = format!("{}-", word.chars().next().unwrap()); let first_char_stutter = format!("{}-", word.chars().next().unwrap());
word = format!( word = format!(
"{}{}", "{}{}",
first_char_stutter.repeat(seeder.random_int(1, 2)), first_char_stutter.repeat(seeder.random_int(1..2)),
word word
); );
} }
@ -223,15 +216,15 @@ impl UwUify {
let first_char_stutter = format!("{}-", word.chars().next().unwrap()); let first_char_stutter = format!("{}-", word.chars().next().unwrap());
word = format!( word = format!(
"{}{}", "{}{}",
first_char_stutter.repeat(seeder.random_int(1, 2)), first_char_stutter.repeat(seeder.random_int(1..2)),
word word
); );
} }
if random_value <= self.modifiers.faces { if random_value <= self.modifiers.faces {
word = format!("{} {}", FACES[seeder.random_int(0, FACES_SIZE)], word); word = format!("{} {}", FACES[seeder.random_int(0..FACES_SIZE)], word);
} }
if random_value <= self.modifiers.actions { if random_value <= self.modifiers.actions {
word = format!("{} {}", ACTIONS[seeder.random_int(0, ACTIONS_SIZE)], word); word = format!("{} {}", ACTIONS[seeder.random_int(0..ACTIONS_SIZE)], word);
} }
} }

@ -20,19 +20,19 @@ struct Args {
/// The modifier to determine how many words to be uwu'ified /// 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)] #[clap(short, long, value_name = "VALUE", default_value = "1", validator = is_between_zero_and_one, display_order = 4)]
words: f32, words: f64,
/// The modifier for uwu faces e.g hello -> hewwo /// 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)] #[clap(short, long, value_name = "VALUE", default_value = "0.05", validator = is_between_zero_and_one, display_order = 5)]
faces: f32, faces: f64,
/// The modifier for actions e.g *shuffles over* /// 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)] #[clap(short, long, value_name = "VALUE", default_value = "0.125", validator = is_between_zero_and_one, display_order = 6)]
actions: f32, actions: f64,
/// The modifier for stutters e.g b-baka! /// 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)] #[clap(short, long, value_name = "VALUE", default_value = "0.225", validator = is_between_zero_and_one, display_order = 7)]
stutters: f32, stutters: f64,
/// Flag to enable/disable random uwu'ifying /// Flag to enable/disable random uwu'ifying
#[clap(short, long, display_order = 8)] #[clap(short, long, display_order = 8)]
@ -68,16 +68,16 @@ fn main() {
} }
} }
fn is_between_zero_and_one(input: &str) -> Result<(), String> { fn is_between_zero_and_one(input: &str) -> Result<(), &'static str> {
let value = match input.parse::<f32>() { let value = match input.parse::<f32>() {
Ok(value) => value, Ok(value) => value,
Err(_) => return Err(String::from("The value must be a decimal number")), Err(_) => return Err("The value must be a decimal number"),
}; };
if (0.0..=1.0).contains(&value) { if (0.0..=1.0).contains(&value) {
return Ok(()); return Ok(());
} }
Err(String::from("The value must be between 0.0 and 1.0")) Err("The value must be between 0.0 and 1.0")
} }
fn modifiers_supplied_at_runtime( fn modifiers_supplied_at_runtime(

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

@ -1,35 +1,41 @@
use rand::{Rng, rngs::ThreadRng, thread_rng}; use std::hash::Hasher;
use rand_pcg::Pcg32;
use rand_seeder::Seeder; use rand::{
distributions::uniform::{SampleRange, SampleUniform},
Rng, RngCore, SeedableRng,
};
use rand_xoshiro::{Xoshiro256Plus, Xoshiro256PlusPlus};
pub struct UwUSeeder { pub struct UwUSeeder {
seeder: Pcg32, floating: Xoshiro256Plus,
rng: ThreadRng, int: Xoshiro256PlusPlus,
random: bool,
} }
impl UwUSeeder { impl UwUSeeder {
#[inline]
pub fn new(word: &str, random: bool) -> UwUSeeder { pub fn new(word: &str, random: bool) -> UwUSeeder {
let entropy = match random {
true => rand::rngs::OsRng.next_u64(),
false => {
let mut hasher = ahash::AHasher::default();
hasher.write(word.as_bytes());
hasher.finish()
}
};
UwUSeeder { UwUSeeder {
seeder: Seeder::from(word).make_rng(), floating: Xoshiro256Plus::seed_from_u64(entropy),
rng: thread_rng(), int: Xoshiro256PlusPlus::seed_from_u64(entropy),
random,
} }
} }
pub fn random(&mut self) -> f32 { #[inline]
if self.random { pub fn random(&mut self) -> f64 {
self.rng.gen_range(0.0..1.0) f64::from_ne_bytes(self.floating.next_u64().to_ne_bytes())
} else {
self.seeder.gen_range(0.0..1.0)
}
} }
pub fn random_int(&mut self, min: i32, max: i32) -> usize { #[inline]
if self.random { pub fn random_int<T: SampleUniform, R: SampleRange<T>>(&mut self, range: R) -> T {
self.rng.gen_range(min..max) as usize self.int.gen_range(range)
} else {
self.seeder.gen_range(min..max) as usize
}
} }
} }

Loading…
Cancel
Save