//! A set of generic utilities used across the codebase. //! //! It currently includes [`ToTitleCase`], [`get_random_u64`], and [`Bye`]. use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; /// Converts a string to title case. /// /// It removes all duplicate whitespaces and capitalizes the first letter of every word. pub trait ToTitleCase { fn to_title_case(&self) -> String; } impl ToTitleCase for T where T: ToString, { /// Removes duplicated whitespaces and capitalizes the first letter of each word. /// /// Returns: /// [`String`] - The title-cased version of the string. fn to_title_case(&self) -> String { self.to_string() .split_whitespace() .map(|word| { let mut chars = word.chars(); match chars.next() { Some(first) => first.to_uppercase().collect::() + chars.as_str(), None => String::new(), } }) .collect::>() .join(" ") } } /// Returns a random `u64` generated using a cryptographically secure random number generator. /// /// Returns: /// [`u64`] - A randomly generated 64-bit unsigned integer. pub fn get_random_u64() -> u64 { let mut rng = ChaCha20Rng::from_os_rng(); rng.random() } /// Consumes any value. Used to normalize return values when the output of a function doesn't /// matter yet all functions being called return different types. pub trait Bye: Sized { /// Whatever the value is, it consumes it and returns a unit value. Used to remove warnings /// about unused results. fn bye(self) {} } impl Bye for T {} /// Trims a string to a maximum length, adding an ellipsis if it exceeds the limit. /// /// Arguments: /// * `string` - The string to trim with ellipse. /// * `max_length` - The max length the string may have. /// /// Returns: /// [`String`] - The trimmed string with an ellipsis if it exceeded the limit. pub fn ellipse(string: String, max_length: usize) -> String { if string.len() <= max_length { string } else { let len = max_length.saturating_sub(3); format!("{}...", &string[..len]) } } /// Copies the bytes from [`i64`] to [`u64`]. /// /// WARNING: This does NOT keep the same "represented" value. This only keeps the bytes intact, /// e.g. 0b0101 will still be 0b0101, but the value represented will change since the type changes. /// /// Arguments: /// * `i` - The [`i64`] to convert. /// /// Returns: /// [`u64`] - The resulting [`u64`]. pub fn i64_to_u64(i: i64) -> u64 { u64::from_ne_bytes(i.to_ne_bytes()) } /// Copies the bytes from [`u64`] to [`i64`]. /// /// WARNING: This does NOT keep the same "represented" value. This only keeps the bytes intact, /// e.g. 0b0101 will still be 0b0101, but the value represented will change since the type changes. /// /// Arguments: /// * `u` - The [`u64`] to convert. /// /// Returns: /// [`i64`] - The resulting [`i64`]. pub fn u64_to_i64(u: u64) -> i64 { i64::from_ne_bytes(u.to_ne_bytes()) }