use blake3::Hasher as Blake3Hasher;
use chrono::NaiveDate;
use indicatif::{ProgressBar, ProgressStyle};
use std::{
collections::{HashMap, HashSet},
fs::{self, File},
io::{BufReader, Read, Write},
ops::Deref,
path::{self, Path},
process::Command,
str,
};
mod constants;
mod count_digits;
mod iterations;
mod macros;
mod maps;
mod operations;
mod options;
mod random;
mod rounded;
mod separator;
mod slice;
mod strings;
mod traits;
mod unique;
pub use self::{
constants::*, count_digits::*, iterations::*, macros::*, maps::*, operations::*, options::*,
random::*, rounded::*, separator::*, slice::*, strings::*, traits::*, unique::*,
};
pub type MyError = Box<dyn std::error::Error + Send + Sync + 'static>;
pub type MyResult<T> = Result<T, MyError>;
const HEX: [char; 16] = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
];
pub fn clear_terminal_screen() {
let result = if cfg!(target_os = "windows") {
Command::new("cmd").args(["/c", "cls"]).spawn()
} else {
Command::new("tput").arg("reset").spawn()
};
if result.is_err()
&& let Some(esc) = char::from_digit(27, 10)
{
print!("{esc}c");
}
}
pub fn open_file<P>(path: P) -> Result<File, std::io::Error>
where
P: AsRef<Path>,
{
fs::OpenOptions::new()
.read(true)
.write(false)
.create(false)
.open(path)
}
pub trait BytesExtension {
fn trim(&self) -> &Self;
fn to_hex_string(&self) -> String;
}
impl BytesExtension for [u8] {
fn trim(&self) -> &[u8] {
let from = match self.iter().position(|b| !b.is_ascii_whitespace()) {
Some(index) => index,
None => return &self[..0],
};
let to = self.iter().rposition(|b| !b.is_ascii_whitespace()).unwrap();
&self[from..=to]
}
fn to_hex_string(&self) -> String {
self.iter()
.flat_map(|byte| {
let a: char = HEX[(*byte as usize) / 16];
let b: char = HEX[(*byte as usize) % 16];
vec![a, b]
})
.collect()
}
}
#[allow(dead_code)]
pub fn to_vec_string<T>(v: &[T]) -> Vec<String>
where
T: ToString,
{
v.iter().map(|x| x.to_string()).collect()
}
#[allow(dead_code)]
pub fn to_vec_slice<T>(v: &[T]) -> Vec<&str>
where
T: AsRef<str>,
{
v.iter().map(|x| x.as_ref()).collect()
}
pub fn get_naive_date<T>(date: T) -> Option<NaiveDate>
where
T: Deref<Target = str> + std::fmt::Display,
{
let digits: String = date.remove_non_digits();
let ddmmyyyy: &str = if digits.chars_count() >= 8 {
&digits[..8]
} else {
return None;
};
match NaiveDate::parse_from_str(ddmmyyyy, "%-d%-m%Y") {
Ok(dt) => Some(dt),
Err(why) => {
eprintln!("fn get_naive_date()");
eprintln!("Data inválida ou inexistente!");
eprintln!("Erro: {why}");
eprintln!("\t'{date}'");
None
}
}
}
pub fn get_naive_date_v2<T>(date: T) -> Option<NaiveDate>
where
T: Deref<Target = str> + std::fmt::Display,
{
let digits: String = date.remove_non_digits();
let ddmmyyyy: u32 = if digits.chars_count() >= 8 {
digits[..8]
.parse::<u32>()
.expect("fn get_naive_date()\nEsperado um número inteiro com 8 dÃgitos!")
} else {
return None;
};
let day = ddmmyyyy / 1_000_000;
let mmyyyy = ddmmyyyy % 1_000_000;
let month = mmyyyy / 10_000;
let year = mmyyyy % 10_000;
let dt: Option<NaiveDate> = NaiveDate::from_ymd_opt(year as i32, month, day);
if dt.is_none() {
eprintln!("Erro! Data inválida ou inexistente:");
eprintln!("\t'{date}': day: {day} ; month: {month} ; year: {year}");
}
dt
}
pub fn string_to_vec_of_integers<T>(string: T) -> MyResult<Vec<u32>>
where
T: Deref<Target = str>,
{
let vec_u32: Result<Vec<u32>, _> = string .split_whitespace()
.map(|s: &str| s.parse::<u32>())
.collect();
Ok(vec_u32?)
}
pub fn get_progressbar(msg: &'static str, total: usize) -> MyResult<ProgressBar> {
let style = get_style(0, 0, 38)?;
let pb = ProgressBar::new(total.try_into()?);
pb.set_message(msg);
pb.set_style(style);
Ok(pb)
}
pub fn get_style(
template_index: usize,
chars_index: usize,
length: usize,
) -> MyResult<ProgressStyle> {
let template_01 = format!(
"{{msg:{length}}} {{spinner:.green}} [{{wide_bar:.cyan/blue}}] {{percent}}/100% ({{eta}}) [{{elapsed_precise}}]"
);
let template_02 = format!(
"{{msg:{length}}} {{spinner:.green}} [{{wide_bar:.cyan/blue}}] {{percent}}/100% ({{eta}})"
);
let template_03 = format!(
"{{msg:{length}}} {{spinner:.green}} [{{wide_bar:.cyan/blue}}] {{pos}}/{{len}} ({{eta}})"
);
let template_04 =
format!("[{{elapsed_precise}}] {{bar:40.cyan/blue}} {{pos}}/{{len}} {{msg:{length}}}");
let templates = [template_01, template_02, template_03, template_04];
let progress_characters = ["#>-", "## ", "■□ ", "█░-", "🦀👾👻"];
let style: ProgressStyle = ProgressStyle::default_bar()
.template(&templates[template_index])?
.progress_chars(progress_characters[chars_index]);
Ok(style)
}
pub fn my_print<P>(write_buffer: &[u8], path: P) -> MyResult<()>
where
P: AsRef<path::Path>,
{
let mut file = fs::File::create(path)?;
file.write_all(write_buffer)?;
let print_msg = match str::from_utf8(write_buffer) {
Ok(valid_uft8) => valid_uft8,
Err(error) => {
eprintln!("fn my_print()");
eprintln!("Invalid UTF-8 sequence!");
panic!("{error}");
}
};
println!("{print_msg}");
Ok(())
}
pub fn blake3_hash<P>(path: P) -> MyResult<String>
where
P: AsRef<Path> + std::marker::Copy + std::fmt::Debug,
{
let file: File = open_file(path)?;
let mut reader: BufReader<File> = BufReader::new(file);
let mut buffer = [0; 1024];
let mut hasher = Blake3Hasher::new();
loop {
let count = reader.read(&mut buffer)?;
if count == 0 {
break;
}
hasher.update(&buffer[..count]);
}
let hash: String = hasher.finalize().to_string();
Ok(hash)
}
pub fn print_split<T>(values: &[T], size: usize)
where
T: std::fmt::Debug,
{
for value in values.chunks(size) {
let text = value
.iter()
.map(|v| format!("{v:?}"))
.collect::<Vec<String>>()
.join(" ");
println!("{text}");
}
}
pub trait Partition<T> {
fn partition_dup(&self) -> (Vec<T>, Vec<T>);
}
impl<T> Partition<T> for Vec<T>
where
T: Clone + Eq + std::hash::Hash,
{
fn partition_dup(&self) -> (Vec<T>, Vec<T>) {
let mut seen: HashSet<T> = HashSet::new();
let mut filter: HashMap<T, bool> = HashMap::new();
let mut unique = Vec::new();
let mut duplicate = Vec::new();
self.iter().for_each(|item| {
filter.insert(item.clone(), seen.insert(item.clone()));
});
self.iter().for_each(|item| {
let is_unique: bool = filter[item];
if is_unique {
unique.push(item.clone());
} else {
duplicate.push(item.clone());
}
});
(unique, duplicate)
}
}
#[cfg(test)]
mod functions {
use super::*;
use chrono::NaiveDate;
use std::collections::HashMap;
#[test]
fn clear_terminal() {
clear_terminal_screen()
}
#[test]
fn partition_values() {
let vector: Vec<String> = ["aa", "ab", "aa", "aa", "cc", "aa", "ab", "d1"]
.iter()
.map(|s| s.to_string())
.collect();
println!("vector: {vector:?}");
let (u, d) = vector.partition_dup();
println!("unique: {u:?}");
println!("duplicate: {d:?}");
assert_eq!(u, ["cc", "d1"]);
assert_eq!(d, ["aa", "ab", "aa", "aa", "aa", "ab"]);
}
#[test]
fn basic_trimming() {
assert_eq!(b" A ".trim(), b"A");
assert_eq!(b" AB ".trim(), b"AB");
assert_eq!(b"A ".trim(), b"A");
assert_eq!(b"AB ".trim(), b"AB");
assert_eq!(b" A".trim(), b"A");
assert_eq!(b" AB".trim(), b"AB");
assert_eq!(b" A B ".trim(), b"A B");
assert_eq!(b"A B ".trim(), b"A B");
assert_eq!(b" A B".trim(), b"A B");
assert_eq!(b" ".trim(), b"");
assert_eq!(b" ".trim(), b"");
assert_eq!(b"\nA\n".trim(), b"A");
assert_eq!(b"\nA B\r\n".trim(), b"A B");
assert_eq!(b"\r\nA B\r\n".trim(), b"A B");
}
#[test]
fn data_dia_mes_ano() -> MyResult<()> {
for (date, result) in [
("18052022", NaiveDate::from_ymd_opt(2022, 5, 18)),
("15121500", NaiveDate::from_ymd_opt(1500, 12, 15)),
("29021972", NaiveDate::from_ymd_opt(1972, 2, 29)),
("18152022", None),
("29021973", None),
("2921972", None),
("2023", None),
("", None),
] {
println!("date: '{date}' ; result: {result:?}");
let naive_date: Option<NaiveDate> = get_naive_date(date);
assert_eq!(naive_date, result);
}
Ok(())
}
#[test]
fn test_group_anagrams() -> MyResult<()> {
pub fn group_anagrams(strs: Vec<String>) -> Vec<Vec<String>> {
let mut h = HashMap::new();
for s in strs {
let mut key: Vec<char> = s.chars().collect();
key.sort();
h.entry(key).or_insert(Vec::new()).push(s);
}
h.into_values().collect()
}
let text = svec!("abc", "bac", "def", "ab", "tuvxz", "abc", "a", "bca", "fed");
let result = group_anagrams(text);
println!("result: {result:?}");
Ok(())
}
}