use std::borrow::Cow;
use std::str::Utf8Error;
#[cfg(not(target_arch = "wasm32"))]
use std::fs::File;
#[cfg(not(target_arch = "wasm32"))]
use std::io::{self, BufRead};
#[cfg(not(target_arch = "wasm32"))]
use std::path::Path;
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Percentage {
pub number: usize,
pub total: usize,
}
impl Percentage {
#[must_use]
pub fn new(number: usize, total: usize) -> Self {
Self { number, total }
}
#[must_use]
pub fn calculate(&self) -> f32 {
Util::calculate_percentage(self.number, self.total)
}
}
pub struct Util;
impl Util {
#[must_use]
#[allow(clippy::cast_precision_loss)]
pub fn calculate_percentage(number: usize, total: usize) -> f32 {
match total {
0 => 0_f32,
_ => (number as f32 * 100.0) / total as f32,
}
}
pub fn percent_decode(s: &str) -> Result<String, Utf8Error> {
Ok(percent_encoding::percent_decode_str(s)
.decode_utf8()?
.to_string())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where
P: AsRef<Path>,
{
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
#[must_use]
#[allow(clippy::needless_pass_by_value)]
pub fn replace_plus(s: Cow<str>) -> String {
s.replace('+', " ")
}
#[must_use]
pub fn str_remove_spaces(s: &str) -> String {
s.replace(' ', "")
}
#[must_use]
pub fn str_splitter(s: &str, splitter: &str) -> Vec<String> {
s.split(splitter)
.map(std::string::ToString::to_string)
.collect()
}
#[must_use]
pub fn str_len_splitter(s: &str, on: usize) -> String {
s.chars()
.enumerate()
.flat_map(|(i, c)| {
if i != 0 && i % on == 0 {
Some(' ')
} else {
None
}
.into_iter()
.chain(std::iter::once(c))
})
.collect::<String>()
}
}
#[cfg(test)]
#[allow(non_snake_case)]
mod util__tests {
use super::*;
#[test]
fn percent() {
let percentage = Util::calculate_percentage(48, 2_598_960);
assert_eq!("0.00185%", format!("{:.5}%", percentage));
assert_eq!(
"0.00000%",
format!("{:.5}%", Util::calculate_percentage(0, 0))
);
}
#[test]
fn percent__zero_numerator() {
let percentage = Util::calculate_percentage(0, 2_598_960);
assert_eq!("0.00000%", format!("{:.5}%", percentage));
}
#[test]
fn percent__zero_denominator() {
let percentage = Util::calculate_percentage(48, 0);
assert_eq!("0.00000%", format!("{:.5}%", percentage));
}
#[test]
fn str_remove_spaces() {
let index = "JJ-22, AQs - ATs,KJs+";
let expected = "JJ-22,AQs-ATs,KJs+".to_string();
assert_eq!(Util::str_remove_spaces(index), expected);
}
#[test]
fn percentage_calculate() {
assert_eq!(25.0_f32, Percentage::new(1, 4).calculate());
assert_eq!(0.0_f32, Percentage::new(0, 0).calculate());
assert_eq!(0.0_f32, Percentage::new(0, 100).calculate());
}
#[test]
fn percent_decode() {
assert_eq!(
"hello world",
Util::percent_decode("hello%20world").unwrap()
);
assert_eq!(
"A♠ J♦",
Util::percent_decode("A%E2%99%A0%20J%E2%99%A6").unwrap()
);
assert_eq!("", Util::percent_decode("").unwrap());
}
#[test]
fn str_splitter() {
assert_eq!(vec!["a", "b", "c"], Util::str_splitter("a,b,c", ","));
assert_eq!(vec!["hello"], Util::str_splitter("hello", ","));
assert_eq!(vec!["", ""], Util::str_splitter(",", ","));
}
#[test]
fn str_len_splitter() {
assert_eq!("AB CD EF", Util::str_len_splitter("ABCDEF", 2));
assert_eq!("ABC DEF", Util::str_len_splitter("ABCDEF", 3));
assert_eq!("ABCDEF", Util::str_len_splitter("ABCDEF", 6));
assert_eq!("A B C", Util::str_len_splitter("ABC", 1));
}
}